---
title: "编写自定义模型字段(model fields)"
version: 3.1
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/3.1/howto/custom-model-fields/
canonical: https://djangodocs.dev/zh-hans/3.1/howto/custom-model-fields/
---
# 编写自定义模型字段(model fields)

## 介绍

[字段参考](/zh-hans/3.1/topics/db/models/) 文档介绍了如何使用 Django 的标准字段类—— [`CharField`](/zh-hans/3.1/ref/models/fields/#django.db.models.CharField)， [`DateField`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField)，等等。大多数情况下，这些类就是你需要的。虽然有时候，Django 的版本不能精确地匹配你的需求，或者你想使用的字段与 Django 内置的完全不同。

Django 内置的字段类型并未覆盖所有可能的数据库字段类型——只有常见的类型，例如 `VARCHAR` 和 `INTEGER`。对于更多模糊的列类型，例如地理多边形（geographic polygons），甚至是用户创建的类型，例如 [PostgreSQL custom types](https://www.postgresql.org/docs/current/sql-createtype.html)，你可以自定义 Django 的 `Field` 子类。

或者，你有一个复杂的 Python 对象，它可以以某种形式序列化，适应标准的数据库列类型。这是另一个 `Field` 子类能帮助你配合模型使用你的对象的示例。

### 我们的示例对象

创建自定义字段要求注意一些细节。为了简化问题，我们在本文档中全程使用同一实例：封装一个 Python 对象，代表手上 [桥牌](https://en.wikipedia.org/wiki/Contract_bridge) 的细节。不要担心，你不需要知道如何玩桥牌就能学习此例子。你只需知道 52 张牌被均分给 4 个玩家，一般称他们 *北*，*东*，*南* 和 *西*。我们的类长这样:

```
class Hand:
    """A hand of cards (bridge style)"""

    def __init__(self, north, east, south, west):
        # Input parameters are lists of cards ('Ah', '9s', etc.)
        self.north = north
        self.east = east
        self.south = south
        self.west = west

    # ... (other possibly useful methods omitted) ...
```

这是一个一般的 Python 类，其中没有特殊的 Django 内容。我们期望在我们的模块中做如下操作 (我们假设模型中的 `hand` 属性是 `Hand` 的一个实例):

```
example = MyModel.objects.get(pk=1)
print(example.hand.north)

new_hand = Hand(north, east, south, west)
example.hand = new_hand
example.save()
```

对模型中的 `hand` 属性的赋值与取值操作与其它 Python 类一直。技巧是告诉 Django 如何保存和加载对象。

为了在模型中使用 `Hand` 类，我们 **不** 需要修改这个类。这很不错，因为这以为着你仅需为已存在的类编写模型支持，即便你不能修改源码。

> **Note**
>
> 你可能只想要自定义数据库列的优点，并在模型中像使用标准 Python 那样；字符串，或浮点数，等等。这种情况与 `Hand` 例子类似，在进行过程中，我们将注意到差异。

## 背后的理论

### 数据库存储

让我们从模型字段开始吧。把它们分开来看，一个模型字段可以在处理数据库时，提供处理一般 Python 对象的方法（字符串，布尔值， `datetime` ，或像 `Hand` 这样更复杂的类型），并将其转换成有用的格式。（对序列化而言这种格式是很有用的，但接下来我们会看到，如果数据库端处于控制之下，序列化会更简单）

模型中的字段必须能以某种方式转换为已存在的数据库列类型。不能的数据库提供不同的可用列类型集，但规则仍相同：你只需要处理这些类型。你想存在数据库中的任何数据都必须能适配这些类型中的某一个。

一般地讲，您要么编写一个 Django 字段来匹配特定的数据库列类型，要么需要一种方法将数据转换为字符串。

对于我们的 `Hand` 示例，我们能将卡片数据转换为一个 104 个字符的字符串，通过以预定义的顺序连接所有卡片——也就是说，先连接 *北* 所拥有的卡，随后是 *东*， *南*，和 *西*。所有 `Hand` 对象能被保存在数据库中的文本或字符列中。

### 一个字段(Field)类做了什么？

所有的 Django 字段（本页提到的 *字段* 均指模型字段，而不是 [表单字段](/zh-hans/3.1/ref/forms/fields/)）都是 [`django.db.models.Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 的子类。对于所有字段，Django 记录的大部分信息是一样的——名字，帮助文本，是否唯一，等等。存储行为由 `Field` 处理。稍后，我们会深入了解 `Field` 能做什么；现在， 可以说万物源于 `Field`，并在其基础上自定义了类的关键行为。

了解 Django 字段类不保存在模型属性中很重要。模型属性包含普通的 Python 对象。你所以定义的字段类实际上在模型类创建时在 `Meta` 类中（这是如何实现的在这里不重要）。这是因为在仅创建和修改属性时，字段类不是必须的。相反，他们提供了属性值间转换的机制，并决定了什么被存入数据库或发送给 [序列化器](/zh-hans/3.1/topics/serialization/)。

在你创建自定义字段时牢记这点。你所写的 Django 的 `Field` 子类提供了多种在 Python 实例和数据库/序列化器之间的转换机制（比如，保存值和使用值进行查询之间是不同的）。听起来有点迷糊，但别担心——通过以下的例子会清晰起来。只要记住，在你需要一个自定义字段时，只需创建两个类：

- 第一个类是用户需要操作的 Python 对象。它们会复制给模型属性，它们会为了显示而读取属性，就想这样。这里本例中的 `Hand` 类。
- 第二类是 `Field` 的子类。这个类知道如何在永久存储格式和 Python 格式之间来回转换。

## 编写一个 field 子类

计划编写第一个 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 子类时，需要先想想新字段和哪个已有的 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 最相似。你会继承 Django 字段节约你的时间吗？如果不会，你需要继承 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 类，从它继承了一切。

初始化新字段有点麻烦，因为要从公共参数中分离你需要的参数，并将剩下的传给父类 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 的 `__init__()` 方法（或你的父类）。

在本例中，我们会调用 `HandField`。（调用你的 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 子类这个主意也很不错，所以认证为一个 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 很简单。）它并不表现的像任何已存在的字段，所以我们将直接继承自 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field):

```
from django.db import models

class HandField(models.Field):

    description = "A hand of cards (bridge style)"

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super().__init__(*args, **kwargs)
```

我们的 `HandField` 接收大多数标准字段选项（参考下面的列表），但是我们确定参数是定长的，因为它只需要保存 52 个卡片和它们的值；总计 104 个字符。

> **Note**
>
> 许多 Django 模型字段可以接受并没有什么用的可选参数。比如，你可以同时将 [`auto_now`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField.auto_now) 和:attr:~django.db.models.DateField.auto\_now 传递给  [`django.db.models.DateField`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField) ，它将无视参数 [`editable`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.editable) （[`auto_now`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField.auto_now) 被设置了就意味着 `editable=False`）。这种情况下不会抛出错误。
>
> 这种行为简化了字段类，因为它不需要检查那些没必要出现的可选参数。它们传递所有可选参数到父类，之后就不再使用它们了。您可以更严格地设置字段可选参数，或者对当前字段设置更放任的行为，一切都有您来决定。

`Field.__init__()` 方法接收以下参数:

- [`verbose_name`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.verbose_name)
- `name`
- [`primary_key`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.primary_key)
- [`max_length`](/zh-hans/3.1/ref/models/fields/#django.db.models.CharField.max_length)
- [`unique`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.unique)
- [`blank`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.blank)
- [`null`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.null)
- [`db_index`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_index)
- `rel`: 用于关联字段（像是： [`ForeignKey`](/zh-hans/3.1/ref/models/fields/#django.db.models.ForeignKey)）。仅用于进阶用途。
- [`default`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.default)
- [`editable`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.editable)
- `serialize`: 若为 `False`，字段传给 Django 的 [序列化器](/zh-hans/3.1/topics/serialization/) 时不会被序列化。默认为 `True`。
- [`unique_for_date`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.unique_for_date)
- [`unique_for_month`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.unique_for_month)
- [`unique_for_year`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.unique_for_year)
- [`choices`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.choices)
- [`help_text`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.help_text)
- [`db_column`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_column)
- [`db_tablespace`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_tablespace): 仅为创建索引，如果后端支持 [tablespaces](/zh-hans/3.1/topics/db/tablespaces/)。一般情况下你可以忽略此选项。
- [`auto_created`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.auto_created):若字段是自动创建的，则为 `True`，用于 [`OneToOneField`](/zh-hans/3.1/ref/models/fields/#django.db.models.OneToOneField) 的模型继承。仅用于进阶用途。

上述列表中所有无解释的选项与在普通 Django 字段中的作用一样。参见 [字段文档](/zh-hans/3.1/ref/models/fields/) 获取例子和细节信息。

### 字段解析

与编写 `__init__()` 方法相对是编写 [`deconstruct()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.deconstruct) 方法。它在 [模型迁移](/zh-hans/3.1/topics/migrations/) 期间告诉 Django 如何获取你的新字段的一个实例，并将其转为序列化形式——特别是，传递什么参数给 `__init__()` 来重新创建它。

如果你未在继承的字段之前添加任何选项，就不需要编写新的 `deconstruct()` 方法。然而，如果你正在修改传递给 `__init__()` 的参数（像 `HandField` 中的一样），你需要增补被传递的值。

`deconstruct()` 返回包含 4 个项目的元组：字段的属性名，字段类的完整导入路径，位置参数（以列表的形式），和关键字参数（以字典的形式）。注意，这与 [为自定义类](/zh-hans/3.1/topics/migrations/#custom-deconstruct-method) 的 `deconstruct()` 方法不同，它返回包含 3个项目的元组。

作为自定义字段的作者，你不需要担心前两个值；基类 `Field` 已包含处理字段属性名和导入路径的代码。然后，你仍必须关注位置参数和关键字参数，这些是你最有可能改的东西。

例如，在 `HandField` 类中，我们总是强制设置 `__init__()` 的长度。基类 `Field` 中的 `deconstruct()` 方法会看到这个值，并尝试在关键字参数中返回它；因此，我们能为了可读性从关键字参数中剔除它:

```
from django.db import models

class HandField(models.Field):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        del kwargs["max_length"]
        return name, path, args, kwargs
```

若你添加了一个新的关键字参数，你需要在 `deconstruct` 中新增代码，将其值传入 `kwargs`。如果不需要字段的重构状态，比如使用默认值的情况，还应该忽略 `kwargs` 中的值。

```
from django.db import models

class CommaSepField(models.Field):
    "Implements comma-separated storage of lists"

    def __init__(self, separator=",", *args, **kwargs):
        self.separator = separator
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        # Only include kwarg if it's not the default
        if self.separator != ",":
            kwargs['separator'] = self.separator
        return name, path, args, kwargs
```

更多的复杂例子超出本文的范围，但是请牢记——对于你的字段实例的任意配置，`deconstruct()` 必须返回能传递给 `__init__` 的参数重构状态。

如果你在父类 `Field` 中设置了新的默认值需要额外注意；说明你希望总是包含它们，而不是在它们采用旧有值时消失。

另外，应当避免用位置参数来返回值，尽可能用关键字参数来返回值以保证在未来能拥有最大的兼容性。如果您在构造方法参数列表中修改参数名称比修改其位置更频繁，您可能会更喜欢位置参数，但请记住，在相当长的一段时间内（也许几年），人们将从序列化版本中重新构建您的字段，这取决于您迁移持续的时间。

您可以在包含字段的迁移文件里查看解析的结果，此外您可以在单元测试中通过解构和重构字段来测试。

```
name, path, args, kwargs = my_field_instance.deconstruct()
new_instance = MyField(*args, **kwargs)
self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)
```

### 修改自定义字段的基类

你可能修改自定义字段的基类，因为 Django 无法检测到修改，并为其实施迁移。例如，如果你先这样:

```
class CustomCharField(models.CharField):
    ...
```

随后决定继承自 `TextField`，你不能像这样修改子类:

```
class CustomCharField(models.TextField):
    ...
```

替代方法是，你必须新建一个自定义字段类，并将你的模型指向此类:

```
class CustomCharField(models.CharField):
    ...

class CustomTextField(models.TextField):
    ...
```

就像文档 [移除字段](/zh-hans/3.1/topics/migrations/#migrations-removing-model-fields) 中讨论的一样，你必须保留原 `CustomCharField` 类只要你还有迁移指向它。

### 为自定义字段编写文档

像之前一样，你需要为自定义字段类型编写文档，这样用户就会知道这他喵到底是啥。除了为其提供对开发者很有用的 docstring 外，你也需要让后台管理员应用程序的用户通过应用程序 django.contrib.admindocs 1 看到一个关于字段类型的简单介绍。只需在自定义字段的 [`description`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.description) 属性提供描述性文本。在上述例子中，由 `admindocs` 应用为 `HandField` 字段提供的描述是 'A hand of cards (bridge style)'。

在 [`django.contrib.admindocs`](/zh-hans/3.1/ref/contrib/admin/admindocs/#module-django.contrib.admindocs) 展示的内容中，字段描述在 `field.__dict__` 中差值，它允许描述包含字段参数。例如， [`CharField`](/zh-hans/3.1/ref/models/fields/#django.db.models.CharField) 的说明是:

```
description = _("String (up to %(max_length)s)")
```

### 实用方法

一旦你已创建了 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 的子类，你可能会考虑重写一些标准方法，这取决于你的字段行为。以下列表中的方法大致按重要性降序排列，即从上至下。

#### 自定义数据库类型

假设你已创建了一个 PostgreSQL 自定义字段，名叫 `mytype`。你可以继承 `Field` 并实现 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 方法，像这样:

```
from django.db import models

class MytypeField(models.Field):
    def db_type(self, connection):
        return 'mytype'
```

只要已建立 `MytypeField`，你就能像使用其它 `Field` 类型一样在模型中使用它:

```
class Person(models.Model):
    name = models.CharField(max_length=80)
    something_else = MytypeField()
```

如果您想创建一个数据库来源不明确的应用，您应该解释各个数据库列类型之间的不同之处。比如，date/time 列类型在 PostgreSQL 中被称为 `timestamp`，而在 MySQL 中则叫 `datetime`。您可以通过 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 方法来检查 `connection.settings_dict['ENGINE']` 属性来进行操作。

例子:

```
class MyDateField(models.Field):
    def db_type(self, connection):
        if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
            return 'datetime'
        else:
            return 'timestamp'
```

[`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 和 [`rel_db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.rel_db_type) 方法由 Django 框架在为应用构建 `CREATE TABLE` 语句时调用——即你第一次创建数据表的时候。这些方法也在构建一个包含此模型字段的 `WHERE` 字句时调用——即你在利用 QuerySet 方法(`get()`, `filter()`, 和 `exclude()`)检出数据时或将此模型字段作为参数时。它们在其它时间不会被调用，故它们能承担执行有点小复杂的代码，例如上述的 `connection.settings_dict` 例子。

某些数据库列类型接受参数，例如 `CHAR(25)`，参数 `25` 表示列的最大长度。类似用例中，该参数若在模型中指定比硬编码在 `db_type()` 方法中更灵活。举个例子，构建 `CharMaxlength25Field` 没多大意义，如下所示:

```
# This is a silly example of hard-coded parameters.
class CharMaxlength25Field(models.Field):
    def db_type(self, connection):
        return 'char(25)'

# In the model:
class MyModel(models.Model):
    # ...
    my_field = CharMaxlength25Field()
```

更好的方式是在运行时指定参数值——即类实例化的时候。需要像这样实现 `Field.__init__()` 即可:

```
# This is a much more flexible example.
class BetterCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(*args, **kwargs)

    def db_type(self, connection):
        return 'char(%s)' % self.max_length

# In the model:
class MyModel(models.Model):
    # ...
    my_field = BetterCharField(25)
```

最后，如果你的列真的要求配置复杂的 SQL，从 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 返回 `None`。这会让 Django 创建 SQL 的代码跳过该字段。随后你需要负责为该字段在正确的表中以某种方式创建列，这种方式允许你告诉 Django 不处理此事。

[`rel_db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.rel_db_type) 方法由字段调用，例如 `ForeignKey` 和 `OneToOneField` ，这些通过指向另一个字段来决定数据库列类型的字段。举个例子，如果你有个 `UnsignedAutoField`，你也需要指向该字段的外键使用相同的数据类型:

```
# MySQL unsigned integer (range 0 to 4294967295).
class UnsignedAutoField(models.AutoField):
    def db_type(self, connection):
        return 'integer UNSIGNED AUTO_INCREMENT'

    def rel_db_type(self, connection):
        return 'integer UNSIGNED'
```

#### 将值转为 Python 对象

若自定义 [`Field`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field) 处理的数据结构比字符串，日期，整型，或浮点型更复杂，你可能需要重写 [`from_db_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.from_db_value) 和 [`to_python()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.to_python)。

若要展示字段的子类， `from_db_value()` 将会在从数据库中载入的生命周期中调用，包括聚集和 [`values()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.values) 调用。

`to_python()` 在反序列化时和为表单应用 [`clean()`](/zh-hans/3.1/ref/models/instances/#django.db.models.Model.clean) 时调用。

作为通用规则， `to_python` 应该平滑地处理以下参数：

- 一个正确的类型（本业持续介绍的例子 `Hand` ）。
- 一个字符串
- `None` （若字段允许 `null=True`）

在 `HandField` 类中，我们在数据库中以 VARCHAR 字段的形式存储数据，所以我们要能在 `from_db_value()` 中处理字符串和 `None`。在 `to_python()` 中，我们也需要处理 `Hand` 实例:

```
import re

from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

def parse_hand(hand_string):
    """Takes a string of cards and splits into a full hand."""
    p1 = re.compile('.{26}')
    p2 = re.compile('..')
    args = [p2.findall(x) for x in p1.findall(hand_string)]
    if len(args) != 4:
        raise ValidationError(_("Invalid input for a Hand instance"))
    return Hand(*args)

class HandField(models.Field):
    # ...

    def from_db_value(self, value, expression, connection):
        if value is None:
            return value
        return parse_hand(value)

    def to_python(self, value):
        if isinstance(value, Hand):
            return value

        if value is None:
            return value

        return parse_hand(value)
```

注意，我们总是为这些方法返回一个 `Hand` 实例。这就是我们要保存在模型属性中的 Python 对象类型。

对于 `to_python()` 来说，如果在值转换过程中出现任何问题，你应该抛出一个 [`ValidationError`](/zh-hans/3.1/ref/exceptions/#django.core.exceptions.ValidationError) 异常。

#### 将 Python 转为查询值

使用数据库需要双向转换，如果你重写了 [`from_db_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.from_db_value) 方法，你也必须重写 [`get_prep_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_prep_value) 将 Python 对象转回查询值。

例子:

```
class HandField(models.Field):
    # ...

    def get_prep_value(self, value):
        return ''.join([''.join(l) for l in (value.north,
                value.east, value.south, value.west)])
```

> **Warning**
>
> 如果你使用了 MySQL 的 `CHAR`，`VARCHAR` 或 `TEXT` 类型，你必须确保 [`get_prep_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_prep_value) 总是返回一个字符串。在 MySQL 中对这些类型操作时非常灵活，甚至有时超出预期，在传入值为正数时，检出结果可能包含非期望的结果。这个问题不会在你总为 [`get_prep_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_prep_value) 返回字符串类型的时候出现。

#### 将查询值转为数据库值

某些数据类型（比如 dates）在数据库后端处理前要转为某种特定格式。 [`get_db_prep_value()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_db_prep_value) 实现了这种转换。查询所以使用的连接由 `connection` 参数指定。这允许你在需要时指定后台要求的转换逻辑。

例如，Django 为其 [`BinaryField`](/zh-hans/3.1/ref/models/fields/#django.db.models.BinaryField) 利用以下方法:

```
def get_db_prep_value(self, value, connection, prepared=False):
    value = super().get_db_prep_value(value, connection, prepared)
    if value is not None:
        return connection.Database.Binary(value)
    return value
```

万一自定义字段需要与普通查询参数使用的转换不同的转换规则，你可以重写 [`get_db_prep_save()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_db_prep_save)。

#### 在保存前预处理数值

如果你要在保存前预处理值，你可以调用 [`pre_save()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.pre_save)。举个例子，Django 的 [`DateTimeField`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateTimeField) 在 [`auto_now`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField.auto_now) 或 [`auto_now_add`](/zh-hans/3.1/ref/models/fields/#django.db.models.DateField.auto_now_add) 中利用此方法正确设置属性。

如果你重写了此方法，你必须在最后返回该属性的值。如果修改了值，那么你也需要更新模型属性，这样持有该引用的模型总会看到正确的值。

#### 为模型字段指定表单字段

为了自定义 [`ModelForm`](/zh-hans/3.1/topics/forms/modelforms/#django.forms.ModelForm) 使用的表单属性，你必须重写 [`formfield()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.formfield)。

表单字段类能通过 `form_class` 和 `choices_form_class` 参数指定；如果字段指定了选项，则使用后者，反之前者。若未提供这些参数，将会使用 [`CharField`](/zh-hans/3.1/ref/forms/fields/#django.forms.CharField) 或 [`TypedChoiceField`](/zh-hans/3.1/ref/forms/fields/#django.forms.TypedChoiceField)。

完整的 `kwargs` 被直接传递给表单字段的 `__init__()` 方法。一般的，你要做的全部工作就是为 `form_class` 参数配置一个合适的默认值，并在随后委托父类处理。这可能要求你编写一个自定义表单字段（甚至表单视图）。查看 [表单文件材料](/zh-hans/3.1/topics/forms/) 获取相关信息。

承接上面的例子，我们能这样编写 [`formfield()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.formfield) 方法:

```
class HandField(models.Field):
    # ...

    def formfield(self, **kwargs):
        # This is a fairly standard way to set up some defaults
        # while letting the caller override them.
        defaults = {'form_class': MyFormField}
        defaults.update(kwargs)
        return super().formfield(**defaults)
```

这假定我们已导入 `MyFormField` 字段类（它有默认视图）。本页文档未覆盖编写自定义表单字段的细节。

#### 仿造内置字段类型

若你已创建了 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 方法，你无需担心 [`get_internal_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_internal_type) 方法——它并不常用。虽然很多时候，数据库存储行为和其他字段类似，所以你能直接用其它字段的逻辑创建正确的列。

例子:

```
class HandField(models.Field):
    # ...

    def get_internal_type(self):
        return 'CharField'
```

无论我们使用了哪个数据库后端， [`migrate`](/zh-hans/3.1/ref/django-admin/#django-admin-migrate) 或其它 SQL 命令总会在保存字符串时为其创建正确的列类型。

若 [`get_internal_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.get_internal_type) 返回了当前数据库后端（即 `django.db.backends.<db_name>.base.DatabaseWrapper.data_types` 中未出现的后端）无法理解的字符串——该字符串仍会被序列化器使用的，但是默认的 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 方法会返回 `None`。查阅文档 [`db_type()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.db_type) 了解为啥有用。如果您打算在 Django 之外的其他地方使用序列化器输出，那么将描述性字符串作为序列化器的字段类型是一个有用的想法。

#### 为序列化转换字段数据

自定义序列化器序列化值的流程，你要重写 [`value_to_string()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.value_to_string)。使用 [`value_to_string()`](/zh-hans/3.1/ref/models/fields/#django.db.models.Field.value_to_string) 是在序列化之前获取字段值的最佳方法。举个例子，由于 `HandField` 使用字符串存储数据，我们能复用一些已有代码:

```
class HandField(models.Field):
    # ...

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return self.get_prep_value(value)
```

### 一些通用建议

编写自定义字段是个棘手的，尤其是在 Python 类，数据库，序列化格式之间进行复杂转换的时候。下面有几个让事情更顺利的建议：

1. 借鉴已有的 Django 字段（位于 `django/db/models/fields/__init__.py`）。试着找到一个与你目标类似的字段，而不是从零开始创建。
2. 为字段类添加一个 `__str__()` 方法。在很多地方，字段代码的默认行为是对值调用 `str()`。（本页文档中， `value` 会是一个 `Hand` 实例，而不是 `HandField`）。所以 `__str()__` 方法会自动将 Python 对象转为字符串格式，帮你剩下不少时间。

## 编写一个 `FileField` 子类

除了上述方法外，处理文件的字段还有一些必须考虑到的特殊要求。 `FileField` 提供的大部分机制（像是操作数据库存储和检索）能保持不变，让子类面对支持特殊文件的挑战。

Django 提供一个 `File` 类，作为文件内容和文件操作的代理。可以继承该类自定义访问文件的方式，哪些方法是可用的。它位于 `django.db.models.fields.files`，它的默认行为在 [file 文档](/zh-hans/3.1/ref/files/file/) 中介绍。

一旦创建了文件 `File` 子类，必须说明要使用新子类 `FileField`。为此，需要为 `FileField` 的子类的 `attr_class` 属性指定新的文件 `File` 子类。

### 一些建议

除了上述细节，下面还有一些准则，有助于极大地提高字段代码的效率和可读性。

1. Django 的 `ImageField` 的源码（位于 `django/db/models/fields/files.py`）就是个展示如何继承 `FileField` 支持特定文件的不错例子，因为它包含了上述所有技巧。
2. 尽可能的缓存文件属性。因为文件可能保存在远端存储系统中，检出它们会消耗额外的时间，甚至是钱，且不总是必要的。一旦检出某个文件，获取其内容，尽可能缓存所有数据，以减少后续调用再次检索文件的次数。
