# 字段

## 简介

模式中的字段相当于顶点的属性。 例如，`User` 有 4 个字段： `age`, `name`, `username` 和 `created_at`.

![re-fields-properties](https://entgo.io/assets/er_fields_properties.png)

模式的 `Fields` 方法可以返回字段列表。例如:

```go
package schema

import (
    "time"

    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/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),
    }
}
```

默认情况下，所有字段都是必填的，但是可以使用 `Optional` 方法将其设置为可选的。

## 类型

`ent` 目前支持以下类型：

* go 的全部数值类型。如： `int`, `uint8`, `float64` 等等.
* `bool`
* `string`
* `time.Time`
* `[]byte` (只支持 SQL).
* `JSON` (只支持 SQL) - **实验性**.
* `Enum` (只支持 SQL).

```go
package schema

import (
    "time"
    "net/url"

    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/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(),
    }
}
```

要了解有关每种类型如何映射到其数据库类型的更多细节，请转到 [Migration](https://3ks.gitbook.io/ent-zh_cn/qian-yi/migrate) 部分。

## 默认值

**非唯一** 字段支持通过 `.Default` 或 `.UpdateDefault` 方法设置默认值.

```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),
    }
}
```

## 验证器

字段验证器是类型为 `func(T) error` 的函数，其使用模式中的 `Validate` 方法定义，并应用在创建或更新实体之前。

字段验证器支持 `string` 和所有数值类型。

```go
package schema

import (
    "errors"
    "regexp"
    "strings"
    "time"

    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/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
            }),
    }
}
```

## 内建验证器

`ent` 为支持的每种类型提供了一些内建验证器

* 数值类型:
  * `Positive()`
  * `Negative()`
  * `Min(i)` - 验证字段值是否大于 i.
  * `Max(i)` - 验证字段值是否小于 i.
  * `Range(i, j)` - 验证字段值是否在 \[i, j] 区间。
* `string`
  * `MinLen(i)`
  * `MaxLen(i)`
  * `Match(regexp.Regexp)`

## 可选字段

可选字段是实体创建时是非必填的字段，在数据库中将其设置为可空字段。 不同于边，**字段默认是必填的**，但是可以使用 `Optional` 方法将其设为可选字段。

```go
// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("required_name"),
        field.String("optional_name").
            Optional(),
    }
}
```

## 区分零值

`Nillable`, 有时候你想区分字段的零值和 `nil`；例如，你想数据库中某个字段的值，既能为 `0` 又能为 `NULL`。那么 `Nillable` 就是为此而生的。

如果你有一个可选（`Optional`）的类型 `T` ，将其设为 `Nillable` 会在生成的结构体中得到一个 `*T`. 因此，如果数据库中的该字段返回 `NULL`，这个类型值为 `nil`. 否则，他将包含一个指向实际数据的指针。

例如，有如下模式：

```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(),
    }
}
```

`User` 生成的结构体如下：

```go
// 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"`
}
```

## 不可变字段

`Immutable`, 不可变字段是指其值只能在创建实体时赋值的字段。即，不会为该字段生成更新方法。

```go
// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name"),
        field.Time("created_at").
            Default(time.Now),
            Immutable(),
    }
}
```

## 唯一性

可以通过 `Unique` 方法将字段定义为唯一的（唯一索引）。需要注意的是唯一字段不能有默认值。

```go
// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name"),
        field.String("nickname").
            Unique(),
    }
}
```

## 存储键

可以通过 `StorageKey` 方法自定义存储名。 在 SQL 中，会将字段名映射为列名；在 Gremlin 中，会将字段名映射为属性名。

```go
// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").
            StorageKey(`old_name"`),
    }
}
```

## 索引

可以在多个字段或者某些边上添加索引。但是，需要注意的是，目前只有 SQL 支持索引特性。

更多关于索引的内容，可以查阅 [索引](https://3ks.gitbook.io/ent-zh_cn/schema/schema-indexes) 部分。

## 标签

可以使用 `StructTag` 方法为生成的实体添加标签。 注意，如果没有提供或者提供的标签不包含 `json`, 那么 `entc` 在生成代码时，会添加默认的 `json` 标签。

```go
// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").
            StructTag(`gqlgen:"gql_name"`),
    }
}
```

## 其它结构体字段

默认情况下，`entc` 会根据 `schema.Fields` 方法中配置的字段生成实体模型。 例如，给定如下模式配置：

```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"`),
    }
}
```

生成的实体如下：

```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"`
}
```

为了将 **不存储在数据库中的字段** 生成到实体中，请使用 [外部模板](https://github.com/gorda/ent-doc-zh_cn/tree/4bf1d74f8b406cbbc5cd1d3dfb7aad2b7a710829/schema/code-gen.md#external-templates). 例如：

```
{{ define "model/fields/additional" }}
    {{- if eq $.Name "User" }}
        // StaticField defined by template.
        StaticField string `json:"static,omitempty"`
    {{- end }}
{{ end }}
```

生成的实体如下：

```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` 方法将 String 字段设置为敏感字段。敏感字段不会被打印，在编码时也会被忽略。

注意：敏感字段不能有 struct 标签（StructTag 方法）。

```go
// User schema.
type User struct {
    ent.Schema
}

// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("password").
            Sensitive(),
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://3ks.gitbook.io/ent-zh_cn/schema/schema-fields.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
