mirror of
https://github.com/ent/ent.git
synced 2026-04-28 05:30:56 +03:00
1039 lines
25 KiB
Plaintext
1039 lines
25 KiB
Plaintext
---
|
|
id: schema-fields
|
|
title: Fields
|
|
---
|
|
|
|
import Tabs from '@theme/Tabs';
|
|
import TabItem from '@theme/TabItem';
|
|
|
|
## Quick Summary
|
|
|
|
Fields (or properties) in the schema are the attributes of the node. For example, a `User`
|
|
with 4 fields: `age`, `name`, `username` and `created_at`:
|
|
|
|

|
|
|
|
Fields are returned from the schema using the `Fields` method. For example:
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"time"
|
|
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Int("age"),
|
|
field.String("name"),
|
|
field.String("username").
|
|
Unique(),
|
|
field.Time("created_at").
|
|
Default(time.Now),
|
|
}
|
|
}
|
|
```
|
|
|
|
All fields are required by default, and can be set to optional using the `Optional` method.
|
|
|
|
## Types
|
|
|
|
The following types are currently supported by the framework:
|
|
|
|
- All Go numeric types. Like `int`, `uint8`, `float64`, etc.
|
|
- `bool`
|
|
- `string`
|
|
- `time.Time`
|
|
- `UUID`
|
|
- `[]byte` (SQL only).
|
|
- `JSON` (SQL only).
|
|
- `Enum` (SQL only).
|
|
- `Other` (SQL only).
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"time"
|
|
"net/url"
|
|
|
|
"github.com/google/uuid"
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Int("age").
|
|
Positive(),
|
|
field.Float("rank").
|
|
Optional(),
|
|
field.Bool("active").
|
|
Default(false),
|
|
field.String("name").
|
|
Unique(),
|
|
field.Time("created_at").
|
|
Default(time.Now),
|
|
field.JSON("url", &url.URL{}).
|
|
Optional(),
|
|
field.JSON("strings", []string{}).
|
|
Optional(),
|
|
field.Enum("state").
|
|
Values("on", "off").
|
|
Optional(),
|
|
field.UUID("uuid", uuid.UUID{}).
|
|
Default(uuid.New),
|
|
}
|
|
}
|
|
```
|
|
|
|
To read more about how each type is mapped to its database-type, go to the [Migration](migrate.md) section.
|
|
|
|
## ID Field
|
|
|
|
The `id` field is builtin in the schema and does not need declaration. In SQL-based
|
|
databases, its type defaults to `int` (but can be changed with a [codegen option](code-gen.md#code-generation-options))
|
|
and auto-incremented in the database.
|
|
|
|
In order to configure the `id` field to be unique across all tables, use the
|
|
[WithGlobalUniqueID](migrate.md#universal-ids) option when running schema migration.
|
|
|
|
If a different configuration for the `id` field is needed, or the `id` value should
|
|
be provided on entity creation by the application (e.g. UUID), override the builtin
|
|
`id` configuration. For example:
|
|
|
|
```go
|
|
// Fields of the Group.
|
|
func (Group) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Int("id").
|
|
StructTag(`json:"oid,omitempty"`),
|
|
}
|
|
}
|
|
|
|
// Fields of the Blob.
|
|
func (Blob) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.UUID("id", uuid.UUID{}).
|
|
Default(uuid.New).
|
|
StorageKey("oid"),
|
|
}
|
|
}
|
|
|
|
// Fields of the Pet.
|
|
func (Pet) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("id").
|
|
MaxLen(25).
|
|
NotEmpty().
|
|
Unique().
|
|
Immutable(),
|
|
}
|
|
}
|
|
```
|
|
|
|
If you need to set a custom function to generate IDs, you can use `DefaultFunc`
|
|
to specify a function which will always be ran when the resource is created.
|
|
See the [related FAQ](faq.md#how-do-i-use-a-custom-generator-of-ids) for more information.
|
|
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Int64("id").
|
|
DefaultFunc(func() int64 {
|
|
// An example of a dumb ID generator - use a production-ready alternative instead.
|
|
return time.Now().Unix() << 8 | atomic.AddInt64(&counter, 1) % 256
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Database Type
|
|
|
|
Each database dialect has its own mapping from Go type to database type. For example,
|
|
the MySQL dialect creates `float64` fields as `double` columns in the database. However,
|
|
there is an option to override the default behavior using the `SchemaType` method.
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/dialect"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
// Card schema.
|
|
type Card struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the Card.
|
|
func (Card) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Float("amount").
|
|
SchemaType(map[string]string{
|
|
dialect.MySQL: "decimal(6,2)", // Override MySQL.
|
|
dialect.Postgres: "numeric", // Override Postgres.
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Go Type
|
|
|
|
The default type for fields are the basic Go types. For example, for string fields, the type is `string`,
|
|
and for time fields, the type is `time.Time`. The `GoType` method provides an option to override the
|
|
default ent type with a custom one.
|
|
|
|
The custom type must be either a type that is convertible to the Go basic type, a type that implements the
|
|
[ValueScanner](https://pkg.go.dev/entgo.io/ent/schema/field?tab=doc#ValueScanner) interface, or has an
|
|
[External ValueScanner](#external-valuescanner). Also, if the provided type implements the Validator interface and no validators have been set,
|
|
the type validator will be used.
|
|
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/dialect"
|
|
"entgo.io/ent/schema/field"
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
// Amount is a custom Go type that's convertible to the basic float64 type.
|
|
type Amount float64
|
|
|
|
// Card schema.
|
|
type Card struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the Card.
|
|
func (Card) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Float("amount").
|
|
GoType(Amount(0)),
|
|
field.String("name").
|
|
Optional().
|
|
// A ValueScanner type.
|
|
GoType(&sql.NullString{}),
|
|
field.Enum("role").
|
|
// A convertible type to string.
|
|
GoType(role.Role("")),
|
|
field.Float("decimal").
|
|
// A ValueScanner type mixed with SchemaType.
|
|
GoType(decimal.Decimal{}).
|
|
SchemaType(map[string]string{
|
|
dialect.MySQL: "decimal(6,2)",
|
|
dialect.Postgres: "numeric",
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
#### External `ValueScanner`
|
|
|
|
Ent allows attaching custom `ValueScanner` for basic or custom Go types. This enables the use of standard
|
|
schema fields while maintaining control over how they are stored in the database without implementing a `ValueScanner`
|
|
interface. Additionally, this option enables users to use `GoType` that does not implement the `ValueScanner`, such
|
|
as `*url.URL`.
|
|
|
|
:::note
|
|
At this stage, this option is only available for text and numeric fields, but it will be extended to other types in
|
|
the future.
|
|
:::
|
|
|
|
<Tabs>
|
|
<TabItem value="TextMarshaller">
|
|
|
|
Fields with a custom Go type that implements the `encoding.TextMarshaller` and `encoding.TextUnmarshaller` interfaces can
|
|
use the `field.TextValueScanner` as a `ValueScanner`. This `ValueScanner` calls `MarshalText` and `UnmarshalText` for
|
|
writing and reading field values from the database:
|
|
|
|
```go
|
|
field.String("big_int").
|
|
GoType(&big.Int{}).
|
|
ValueScanner(field.TextValueScanner[*big.Int]{})
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="BinaryMarshaller">
|
|
|
|
Fields with a custom Go type that implements the `encoding.BinaryMarshaller` and `encoding.BinaryUnmarshaller` interfaces can
|
|
use the `field.BinaryValueScanner` as a `ValueScanner`. This `ValueScanner` calls `MarshalBinary` and `UnmarshalBinary` for
|
|
writing and reading field values from the database:
|
|
|
|
```go
|
|
field.String("url").
|
|
GoType(&url.URL{}).
|
|
ValueScanner(field.BinaryValueScanner[*url.URL]{})
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="Functions based">
|
|
|
|
The `field.ValueScannerFunc` allows setting two functions to be used for writing and reading database values: `V`
|
|
for `driver.Value` and `S` for `sql.Scanner`:
|
|
|
|
```go
|
|
field.String("encoded").
|
|
ValueScanner(field.ValueScannerFunc[string, *sql.NullString]{
|
|
V: func(s string) (driver.Value, error) {
|
|
return base64.StdEncoding.EncodeToString([]byte(s)), nil
|
|
},
|
|
S: func(ns *sql.NullString) (string, error) {
|
|
if !ns.Valid {
|
|
return "", nil
|
|
}
|
|
b, err := base64.StdEncoding.DecodeString(ns.String)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
},
|
|
})
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="Custom">
|
|
|
|
```go title="usage"
|
|
field.String("prefixed").
|
|
ValueScanner(PrefixedHex{
|
|
prefix: "0x",
|
|
})
|
|
```
|
|
|
|
```go title="implementation"
|
|
|
|
// PrefixedHex is a custom type that implements the TypeValueScanner interface.
|
|
type PrefixedHex struct {
|
|
prefix string
|
|
}
|
|
|
|
// Value implements the TypeValueScanner.Value method.
|
|
func (p PrefixedHex) Value(s string) (driver.Value, error) {
|
|
return p.prefix + ":" + hex.EncodeToString([]byte(s)), nil
|
|
}
|
|
|
|
// ScanValue implements the TypeValueScanner.ScanValue method.
|
|
func (PrefixedHex) ScanValue() field.ValueScanner {
|
|
return &sql.NullString{}
|
|
}
|
|
|
|
// FromValue implements the TypeValueScanner.FromValue method.
|
|
func (p PrefixedHex) FromValue(v driver.Value) (string, error) {
|
|
s, ok := v.(*sql.NullString)
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected input for FromValue: %T", v)
|
|
}
|
|
if !s.Valid {
|
|
return "", nil
|
|
}
|
|
d, err := hex.DecodeString(strings.TrimPrefix(s.String, p.prefix+":"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(d), nil
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Other Field
|
|
|
|
Other represents a field that is not a good fit for any of the standard field types.
|
|
Examples are a Postgres Range type or Geospatial type
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/dialect"
|
|
"entgo.io/ent/schema/field"
|
|
|
|
"github.com/jackc/pgtype"
|
|
)
|
|
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Other("duration", &pgtype.Tstzrange{}).
|
|
SchemaType(map[string]string{
|
|
dialect.Postgres: "tstzrange",
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Default Values
|
|
|
|
**Non-unique** fields support default values using the `Default` and `UpdateDefault` methods.
|
|
You can also specify `DefaultFunc` instead to have a custom generator.
|
|
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Time("created_at").
|
|
Default(time.Now),
|
|
field.Time("updated_at").
|
|
Default(time.Now).
|
|
UpdateDefault(time.Now),
|
|
field.String("name").
|
|
Default("unknown"),
|
|
field.String("cuid").
|
|
DefaultFunc(cuid.New),
|
|
field.JSON("dirs", []http.Dir{}).
|
|
Default([]http.Dir{"/tmp"}),
|
|
}
|
|
}
|
|
```
|
|
|
|
SQL-specific literals or expressions like function calls can be added to default value configuration using the
|
|
[`entsql.Annotation`](https://pkg.go.dev/entgo.io/ent@master/dialect/entsql#Annotation):
|
|
|
|
```go {9,16,23-27}
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
// Add a new field with CURRENT_TIMESTAMP
|
|
// as a default value to all previous rows.
|
|
field.Time("created_at").
|
|
Default(time.Now).
|
|
Annotations(
|
|
entsql.Default("CURRENT_TIMESTAMP"),
|
|
),
|
|
// Add a new field with a default value
|
|
// expression that works on all dialects.
|
|
field.String("field").
|
|
Optional().
|
|
Annotations(
|
|
entsql.DefaultExpr("lower(other_field)"),
|
|
),
|
|
// Add a new field with custom default value
|
|
// expression for each dialect.
|
|
field.String("default_exprs").
|
|
Optional().
|
|
Annotations(
|
|
entsql.DefaultExprs(map[string]string{
|
|
dialect.MySQL: "TO_BASE64('ent')",
|
|
dialect.SQLite: "hex('ent')",
|
|
dialect.Postgres: "md5('ent')",
|
|
}),
|
|
),
|
|
}
|
|
}
|
|
```
|
|
|
|
In case your `DefaultFunc` is also returning an error, it is better to handle it properly using [schema-hooks](hooks.md#schema-hooks).
|
|
See [this FAQ](faq.md#how-to-use-a-custom-generator-of-ids) for more information.
|
|
|
|
## Validators
|
|
|
|
A field validator is a function from type `func(T) error` that is defined in the schema
|
|
using the `Validate` method, and applied on the field value before creating or updating
|
|
the entity.
|
|
|
|
The supported types of field validators are `string` and all numeric types.
|
|
|
|
```go
|
|
package schema
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"entgo.io/ent"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
|
|
// Group schema.
|
|
type Group struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the group.
|
|
func (Group) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("name").
|
|
Match(regexp.MustCompile("[a-zA-Z_]+$")).
|
|
Validate(func(s string) error {
|
|
if strings.ToLower(s) == s {
|
|
return errors.New("group name must begin with uppercase")
|
|
}
|
|
return nil
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
Here is another example for writing a reusable validator:
|
|
|
|
```go
|
|
import (
|
|
"entgo.io/ent/dialect/entsql"
|
|
"entgo.io/ent/schema/field"
|
|
)
|
|
|
|
// MaxRuneCount validates the rune length of a string by using the unicode/utf8 package.
|
|
func MaxRuneCount(maxLen int) func(s string) error {
|
|
return func(s string) error {
|
|
if utf8.RuneCountInString(s) > maxLen {
|
|
return errors.New("value is more than the max length")
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
field.String("name").
|
|
// If using a SQL-database: change the underlying data type to varchar(10).
|
|
Annotations(entsql.Annotation{
|
|
Size: 10,
|
|
}).
|
|
Validate(MaxRuneCount(10))
|
|
field.String("nickname").
|
|
// If using a SQL-database: change the underlying data type to varchar(20).
|
|
Annotations(entsql.Annotation{
|
|
Size: 20,
|
|
}).
|
|
Validate(MaxRuneCount(20))
|
|
```
|
|
|
|
## Built-in Validators
|
|
|
|
The framework provides a few built-in validators for each type:
|
|
|
|
- Numeric types:
|
|
- `Positive()`
|
|
- `Negative()`
|
|
- `NonNegative()`
|
|
- `Min(i)` - Validate that the given value is > i.
|
|
- `Max(i)` - Validate that the given value is < i.
|
|
- `Range(i, j)` - Validate that the given value is within the range [i, j].
|
|
|
|
- `string`
|
|
- `MinLen(i)`
|
|
- `MaxLen(i)`
|
|
- `Match(regexp.Regexp)`
|
|
- `NotEmpty`
|
|
|
|
- `[]byte`
|
|
- `MaxLen(i)`
|
|
- `MinLen(i)`
|
|
- `NotEmpty`
|
|
|
|
## Optional
|
|
|
|
Optional fields are fields that are not required in the entity creation, and
|
|
will be set to nullable fields in the database.
|
|
Unlike edges, **fields are required by default**, and setting them to
|
|
optional should be done explicitly using the `Optional` method.
|
|
|
|
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("required_name"),
|
|
field.String("optional_name").
|
|
Optional(),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Nillable
|
|
Sometimes you want to be able to distinguish between the zero value of fields and `nil`.
|
|
For example, if the database column contains `0` or `NULL`. The `Nillable` option exists exactly for this.
|
|
|
|
If you have an `Optional` field of type `T`, setting it to `Nillable` will generate
|
|
a struct field with type `*T`. Hence, if the database returns `NULL` for this field,
|
|
the struct field will be `nil`. Otherwise, it will contain a pointer to the actual value.
|
|
|
|
For example, given this schema:
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("required_name"),
|
|
field.String("optional_name").
|
|
Optional(),
|
|
field.String("nillable_name").
|
|
Optional().
|
|
Nillable(),
|
|
}
|
|
}
|
|
```
|
|
|
|
The generated struct for the `User` entity will be as follows:
|
|
|
|
```go title="ent/user.go"
|
|
package ent
|
|
|
|
// User entity.
|
|
type User struct {
|
|
RequiredName string `json:"required_name,omitempty"`
|
|
OptionalName string `json:"optional_name,omitempty"`
|
|
NillableName *string `json:"nillable_name,omitempty"`
|
|
}
|
|
```
|
|
|
|
#### `Nillable` required fields
|
|
|
|
`Nillable` fields are also helpful for avoiding zero values in JSON marshaling for fields that have not been
|
|
`Select`ed in the query. For example, a `time.Time` field.
|
|
|
|
```go
|
|
// Fields of the task.
|
|
func (Task) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Time("created_at").
|
|
Default(time.Now),
|
|
field.Time("nillable_created_at").
|
|
Default(time.Now).
|
|
Nillable(),
|
|
}
|
|
}
|
|
```
|
|
|
|
The generated struct for the `Task` entity will be as follows:
|
|
|
|
```go title="ent/task.go"
|
|
package ent
|
|
|
|
// Task entity.
|
|
type Task struct {
|
|
// CreatedAt holds the value of the "created_at" field.
|
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
|
// NillableCreatedAt holds the value of the "nillable_created_at" field.
|
|
NillableCreatedAt *time.Time `json:"nillable_created_at,omitempty"`
|
|
}
|
|
```
|
|
|
|
And the result of `json.Marshal` is:
|
|
|
|
```go
|
|
b, _ := json.Marshal(Task{})
|
|
fmt.Printf("%s\n", b)
|
|
//highlight-next-line-info
|
|
// {"created_at":"0001-01-01T00:00:00Z"}
|
|
|
|
now := time.Now()
|
|
b, _ = json.Marshal(Task{CreatedAt: now, NillableCreatedAt: &now})
|
|
fmt.Printf("%s\n", b)
|
|
//highlight-next-line-info
|
|
// {"created_at":"2009-11-10T23:00:00Z","nillable_created_at":"2009-11-10T23:00:00Z"}
|
|
```
|
|
|
|
## Immutable
|
|
|
|
Immutable fields are fields that can be set only in the creation of the entity.
|
|
i.e., no setters will be generated for the update builders of the entity.
|
|
|
|
```go {6}
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Time("created_at").
|
|
Default(time.Now).
|
|
Immutable(),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Uniqueness
|
|
Fields can be defined as unique using the `Unique` method.
|
|
Note that unique fields cannot have default values.
|
|
|
|
```go {5}
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("nickname").
|
|
Unique(),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Comments
|
|
|
|
A comment can be added to a field using the `.Comment()` method. This comment
|
|
appears before the field in the generated entity code. Newlines are supported
|
|
using the `\n` escape sequence.
|
|
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("name").
|
|
Default("John Doe").
|
|
Comment("Name of the user.\n If not specified, defaults to \"John Doe\"."),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Deprecated Fields
|
|
|
|
The `Deprecated` method can be used to mark a field as deprecated. Deprecated fields are not
|
|
selected by default in queries, and their struct fields are annotated as `Deprecated` in the
|
|
generated code.
|
|
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("name").
|
|
Deprecated("use `full_name` instead"),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Storage Key
|
|
|
|
Custom storage name can be configured using the `StorageKey` method.
|
|
It's mapped to a column name in SQL dialects and to property name in Gremlin.
|
|
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("name").
|
|
StorageKey("old_name"),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Indexes
|
|
Indexes can be defined on multi fields and some types of edges as well.
|
|
However, you should note, that this is currently an SQL-only feature.
|
|
|
|
Read more about this in the [Indexes](schema-indexes.md) section.
|
|
|
|
## Struct Tags
|
|
|
|
Custom struct tags can be added to the generated entities using the `StructTag`
|
|
method. Note that if this option was not provided, or provided and did not
|
|
contain the `json` tag, the default `json` tag will be added with the field name.
|
|
|
|
```go
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("name").
|
|
StructTag(`gqlgen:"gql_name"`),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Additional Struct Fields
|
|
|
|
By default, `ent` generates the entity model with fields that are configured in the `schema.Fields` method.
|
|
For example, given this schema configuration:
|
|
|
|
```go
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Int("age").
|
|
Optional().
|
|
Nillable(),
|
|
field.String("name").
|
|
StructTag(`gqlgen:"gql_name"`),
|
|
}
|
|
}
|
|
```
|
|
|
|
The generated model will be as follows:
|
|
|
|
```go
|
|
// User is the model entity for the User schema.
|
|
type User struct {
|
|
// Age holds the value of the "age" field.
|
|
Age *int `json:"age,omitempty"`
|
|
// Name holds the value of the "name" field.
|
|
Name string `json:"name,omitempty" gqlgen:"gql_name"`
|
|
}
|
|
```
|
|
|
|
In order to add additional fields to the generated struct **that are not stored in the database**,
|
|
use [external templates](code-gen.md/#external-templates). For example:
|
|
|
|
```gotemplate
|
|
{{ define "model/fields/additional" }}
|
|
{{- if eq $.Name "User" }}
|
|
// StaticField defined by template.
|
|
StaticField string `json:"static,omitempty"`
|
|
{{- end }}
|
|
{{ end }}
|
|
```
|
|
|
|
The generated model will be as follows:
|
|
|
|
```go
|
|
// User is the model entity for the User schema.
|
|
type User struct {
|
|
// Age holds the value of the "age" field.
|
|
Age *int `json:"age,omitempty"`
|
|
// Name holds the value of the "name" field.
|
|
Name string `json:"name,omitempty" gqlgen:"gql_name"`
|
|
// StaticField defined by template.
|
|
StaticField string `json:"static,omitempty"`
|
|
}
|
|
```
|
|
|
|
## Sensitive Fields
|
|
|
|
String fields can be defined as sensitive using the `Sensitive` method. Sensitive fields
|
|
won't be printed and they will be omitted when encoding.
|
|
|
|
Note that sensitive fields cannot have struct tags.
|
|
|
|
```go
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("password").
|
|
Sensitive(),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Enum Fields
|
|
|
|
The `Enum` builder allows creating enum fields with a list of permitted values.
|
|
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("first_name"),
|
|
field.String("last_name"),
|
|
field.Enum("size").
|
|
Values("big", "small"),
|
|
}
|
|
}
|
|
```
|
|
|
|
:::info [Using PostgreSQL Native Enum Types](/docs/migration/enum-types)
|
|
By default, Ent uses simple string types to represent the enum values in **PostgreSQL and SQLite**. However, in some
|
|
cases, you may want to use the native enum types provided by the database. Follow the [enum migration guide](/docs/migration/enum-types)
|
|
for more info.
|
|
:::
|
|
|
|
When a custom [`GoType`](#go-type) is being used, it must be convertible to the basic `string` type or it needs to implement the [ValueScanner](https://pkg.go.dev/entgo.io/ent/schema/field#ValueScanner) interface.
|
|
|
|
The [EnumValues](https://pkg.go.dev/entgo.io/ent/schema/field#EnumValues) interface is also required by the custom Go type to tell Ent what are the permitted values of the enum.
|
|
|
|
The following example shows how to define an `Enum` field with a custom Go type that is convertible to `string`:
|
|
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("first_name"),
|
|
field.String("last_name"),
|
|
// A convertible type to string.
|
|
field.Enum("shape").
|
|
GoType(property.Shape("")),
|
|
}
|
|
}
|
|
```
|
|
|
|
Implement the [EnumValues](https://pkg.go.dev/entgo.io/ent/schema/field#EnumValues) interface.
|
|
```go
|
|
package property
|
|
|
|
type Shape string
|
|
|
|
const (
|
|
Triangle Shape = "TRIANGLE"
|
|
Circle Shape = "CIRCLE"
|
|
)
|
|
|
|
// Values provides list valid values for Enum.
|
|
func (Shape) Values() (kinds []string) {
|
|
for _, s := range []Shape{Triangle, Circle} {
|
|
kinds = append(kinds, string(s))
|
|
}
|
|
return
|
|
}
|
|
|
|
```
|
|
The following example shows how to define an `Enum` field with a custom Go type that is not convertible to `string`, but it implements the [ValueScanner](https://pkg.go.dev/entgo.io/ent/schema/field#ValueScanner) interface:
|
|
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("first_name"),
|
|
field.String("last_name"),
|
|
// Add conversion to and from string
|
|
field.Enum("level").
|
|
GoType(property.Level(0)),
|
|
}
|
|
}
|
|
```
|
|
Implement also the [ValueScanner](https://pkg.go.dev/entgo.io/ent/schema/field?tab=doc#ValueScanner) interface.
|
|
|
|
```go
|
|
package property
|
|
|
|
import "database/sql/driver"
|
|
|
|
type Level int
|
|
|
|
const (
|
|
Unknown Level = iota
|
|
Low
|
|
High
|
|
)
|
|
|
|
func (p Level) String() string {
|
|
switch p {
|
|
case Low:
|
|
return "LOW"
|
|
case High:
|
|
return "HIGH"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
// Values provides list valid values for Enum.
|
|
func (Level) Values() []string {
|
|
return []string{Unknown.String(), Low.String(), High.String()}
|
|
}
|
|
|
|
// Value provides the DB a string from int.
|
|
func (p Level) Value() (driver.Value, error) {
|
|
return p.String(), nil
|
|
}
|
|
|
|
// Scan tells our code how to read the enum into our type.
|
|
func (p *Level) Scan(val any) error {
|
|
var s string
|
|
switch v := val.(type) {
|
|
case nil:
|
|
return nil
|
|
case string:
|
|
s = v
|
|
case []uint8:
|
|
s = string(v)
|
|
}
|
|
switch s {
|
|
case "LOW":
|
|
*p = Low
|
|
case "HIGH":
|
|
*p = High
|
|
default:
|
|
*p = Unknown
|
|
}
|
|
return nil
|
|
}
|
|
```
|
|
|
|
Combining it all together:
|
|
```go
|
|
// Fields of the User.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.String("first_name"),
|
|
field.String("last_name"),
|
|
field.Enum("size").
|
|
Values("big", "small"),
|
|
// A convertible type to string.
|
|
field.Enum("shape").
|
|
GoType(property.Shape("")),
|
|
// Add conversion to and from string.
|
|
field.Enum("level").
|
|
GoType(property.Level(0)),
|
|
}
|
|
}
|
|
```
|
|
|
|
After code generation usage is trivial:
|
|
```go
|
|
client.User.Create().
|
|
SetFirstName("John").
|
|
SetLastName("Dow").
|
|
SetSize(user.SizeSmall).
|
|
SetShape(property.Triangle).
|
|
SetLevel(property.Low).
|
|
SaveX(context.Background())
|
|
|
|
john := client.User.Query().FirstX(context.Background())
|
|
fmt.Println(john)
|
|
// User(id=1, first_name=John, last_name=Dow, size=small, shape=TRIANGLE, level=LOW)
|
|
```
|
|
|
|
## Annotations
|
|
|
|
`Annotations` is used to attach arbitrary metadata to the field object in code generation.
|
|
Template extensions can retrieve this metadata and use it inside their templates.
|
|
|
|
Note that the metadata object must be serializable to a JSON raw value (e.g. struct, map or slice).
|
|
|
|
```go
|
|
// User schema.
|
|
type User struct {
|
|
ent.Schema
|
|
}
|
|
|
|
// Fields of the user.
|
|
func (User) Fields() []ent.Field {
|
|
return []ent.Field{
|
|
field.Time("creation_date").
|
|
Annotations(entgql.Annotation{
|
|
OrderField: "CREATED_AT",
|
|
}),
|
|
}
|
|
}
|
|
```
|
|
|
|
Read more about annotations and their usage in templates in the [template doc](templates.md#annotations).
|
|
|
|
## Naming Convention
|
|
|
|
By convention field names should use `snake_case`. The corresponding struct fields generated by `ent` will follow the Go convention
|
|
of using `PascalCase`. In cases where `PascalCase` is desired, you can do so with the `StorageKey` or `StructTag` methods.
|