{"title":"编写自定义模型字段","version":"5.2","locale":"zh-hans","docname":"howto/custom-model-fields","url":"/zh-hans/5.2/howto/custom-model-fields/","canonical":"https://djangodocs.dev/zh-hans/5.2/howto/custom-model-fields/","summary":"介绍 Link to this heading # 字段参考 文档介绍了如何使用 Django 的标准字段类—— CharField ， DateField ，等等。大多数情况下，这些类就是你需要的。虽然有时候，Django 的版本不能精确地匹配你的需求，或者你想使用的字段与 Django 内置的完全不同。 Django…","html":"<h1>编写自定义模型字段<a class=\"heading-anchor\" href=\"#how-to-create-custom-model-fields\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<section id=\"introduction\">\n<h2>介绍<a class=\"heading-anchor\" href=\"#introduction\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><a class=\"reference internal\" href=\"/zh-hans/5.2/topics/db/models/\"><span class=\"doc\">字段参考</span></a> 文档介绍了如何使用 Django 的标准字段类—— <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.CharField\" title=\"django.db.models.CharField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">CharField</span></code></a>， <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField\" title=\"django.db.models.DateField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DateField</span></code></a>，等等。大多数情况下，这些类就是你需要的。虽然有时候，Django 的版本不能精确地匹配你的需求，或者你想使用的字段与 Django 内置的完全不同。</p>\n<p>Django 内置的字段类型并未覆盖所有可能的数据库字段类型——只有常见的类型，例如 <code class=\"docutils literal notranslate\"><span class=\"pre\">VARCHAR</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">INTEGER</span></code>。对于更多模糊的列类型，例如地理多边形（geographic polygons），甚至是用户创建的类型，例如 <a class=\"reference external\" href=\"https://www.postgresql.org/docs/current/sql-createtype.html\">PostgreSQL custom types</a>，你可以自定义 Django 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 子类。</p>\n<p>或者，你有一个复杂的 Python 对象，它可以以某种形式序列化，适应标准的数据库列类型。这是另一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 子类能帮助你配合模型使用你的对象的示例。</p>\n<section id=\"our-example-object\">\n<h3>我们的示例对象<a class=\"heading-anchor\" href=\"#our-example-object\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>创建自定义字段要求注意一些细节。为了简化问题，我们在本文档中全程使用同一实例：封装一个 Python 对象，代表手上 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Contract_bridge\">桥牌</a> 的细节。不要担心，你不需要知道如何玩桥牌就能学习此例子。你只需知道 52 张牌被均分给 4 个玩家，一般称他们 <em>北</em>，<em>东</em>，<em>南</em> 和 <em>西</em>。我们的类长这样:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Hand</span><span class=\"p\">:</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;A hand of cards (bridge style)&quot;&quot;&quot;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">north</span><span class=\"p\">,</span> <span class=\"n\">east</span><span class=\"p\">,</span> <span class=\"n\">south</span><span class=\"p\">,</span> <span class=\"n\">west</span><span class=\"p\">):</span>\n        <span class=\"c1\"># Input parameters are lists of cards (&#39;Ah&#39;, &#39;9s&#39;, etc.)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">north</span> <span class=\"o\">=</span> <span class=\"n\">north</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">east</span> <span class=\"o\">=</span> <span class=\"n\">east</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">south</span> <span class=\"o\">=</span> <span class=\"n\">south</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">west</span> <span class=\"o\">=</span> <span class=\"n\">west</span>\n\n    <span class=\"c1\"># ... (other possibly useful methods omitted) ...</span>\n</code></pre></div>\n<p>这是一个一般的 Python 类，其中没有特殊的 Django 内容。我们期望在我们的模块中做如下操作 (我们假设模型中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">hand</span></code> 属性是 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 的一个实例):</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">example</span> <span class=\"o\">=</span> <span class=\"n\">MyModel</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"n\">example</span><span class=\"o\">.</span><span class=\"n\">hand</span><span class=\"o\">.</span><span class=\"n\">north</span><span class=\"p\">)</span>\n\n<span class=\"n\">new_hand</span> <span class=\"o\">=</span> <span class=\"n\">Hand</span><span class=\"p\">(</span><span class=\"n\">north</span><span class=\"p\">,</span> <span class=\"n\">east</span><span class=\"p\">,</span> <span class=\"n\">south</span><span class=\"p\">,</span> <span class=\"n\">west</span><span class=\"p\">)</span>\n<span class=\"n\">example</span><span class=\"o\">.</span><span class=\"n\">hand</span> <span class=\"o\">=</span> <span class=\"n\">new_hand</span>\n<span class=\"n\">example</span><span class=\"o\">.</span><span class=\"n\">save</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>对模型中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">hand</span></code> 属性的赋值与取值操作与其它 Python 类一直。技巧是告诉 Django 如何保存和加载对象。</p>\n<p>为了在模型中使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 类，我们 <strong>不</strong> 需要修改这个类。这很不错，因为这以为着你仅需为已存在的类编写模型支持，即便你不能修改源码。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>你可能只想要自定义数据库列的优点，并在模型中像使用标准 Python 那样；字符串，或浮点数，等等。这种情况与 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 例子类似，在进行过程中，我们将注意到差异。</p>\n</aside>\n</section>\n</section>\n<section id=\"background-theory\">\n<h2>背后的理论<a class=\"heading-anchor\" href=\"#background-theory\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"database-storage\">\n<h3>数据库存储<a class=\"heading-anchor\" href=\"#database-storage\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>让我们从模型字段开始吧。把它们分开来看，一个模型字段可以在处理数据库时，提供处理一般 Python 对象的方法（字符串，布尔值， <code class=\"docutils literal notranslate\"><span class=\"pre\">datetime</span></code> ，或像 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 这样更复杂的类型），并将其转换成有用的格式。（对序列化而言这种格式是很有用的，但接下来我们会看到，如果数据库端处于控制之下，序列化会更简单）</p>\n<p>模型中的字段必须能以某种方式转换为已存在的数据库列类型。不能的数据库提供不同的可用列类型集，但规则仍相同：你只需要处理这些类型。你想存在数据库中的任何数据都必须能适配这些类型中的某一个。</p>\n<p>一般地讲，您要么编写一个 Django 字段来匹配特定的数据库列类型，要么需要一种方法将数据转换为字符串。</p>\n<p>对于我们的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 示例，我们可以通过将所有卡片按预定的顺序连接在一起来将卡片数据转换为一个包含 104 个字符的字符串，例如，首先是所有 <em>北方</em> 的卡片，然后是 <em>东方</em>、<em>南方</em> 和 <em>西方</em> 的卡片。因此，<code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 对象可以保存到数据库中的文本或字符列中。</p>\n</section>\n<section id=\"what-does-a-field-class-do\">\n<h3>一个字段(Field)类做了什么？<a class=\"heading-anchor\" href=\"#what-does-a-field-class-do\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>所有的 Django 字段（本页提到的 <em>字段</em> 均指模型字段，而不是 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/forms/fields/\"><span class=\"doc\">表单字段</span></a>）都是 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.db.models.Field</span></code></a> 的子类。对于所有字段，Django 记录的大部分信息是一样的——名字，帮助文本，是否唯一，等等。存储行为由 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 处理。稍后，我们会深入了解 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 能做什么；现在， 可以说万物源于 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code>，并在其基础上自定义了类的关键行为。</p>\n<p>了解 Django 字段类不保存在模型属性中很重要。模型属性包含普通的 Python 对象。你所以定义的字段类实际上在模型类创建时在 <code class=\"docutils literal notranslate\"><span class=\"pre\">Meta</span></code> 类中（这是如何实现的在这里不重要）。这是因为在仅创建和修改属性时，字段类不是必须的。相反，他们提供了属性值间转换的机制，并决定了什么被存入数据库或发送给 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/serialization/\"><span class=\"doc\">序列化器</span></a>。</p>\n<p>在你创建自定义字段时牢记这点。你所写的 Django 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 子类提供了多种在 Python 实例和数据库/序列化器之间的转换机制（比如，保存值和使用值进行查询之间是不同的）。听起来有点迷糊，但别担心——通过以下的例子会清晰起来。只要记住，在你需要一个自定义字段时，只需创建两个类：</p>\n<ul class=\"simple\">\n<li><p>第一个类是用户需要操作的 Python 对象。它们会复制给模型属性，它们会为了显示而读取属性，就想这样。这里本例中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 类。</p></li>\n<li><p>第二类是 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 的子类。这个类知道如何在永久存储格式和 Python 格式之间来回转换。</p></li>\n</ul>\n</section>\n</section>\n<section id=\"writing-a-field-subclass\">\n<h2>编写一个 field 子类<a class=\"heading-anchor\" href=\"#writing-a-field-subclass\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>计划编写第一个 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 子类时，需要先想想新字段和哪个已有的 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 最相似。你会继承 Django 字段节约你的时间吗？如果不会，你需要继承 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 类，从它继承了一切。</p>\n<p>初始化新字段有点麻烦，因为要从公共参数中分离你需要的参数，并将剩下的传给父类 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 方法（或你的父类）。</p>\n<p>在本例中，我们会调用 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code>。（调用你的 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 子类这个主意也很不错，所以认证为一个 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 很简单。）它并不表现的像任何已存在的字段，所以我们将直接继承自 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">models</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"n\">description</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;A hand of cards (bridge style)&quot;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n        <span class=\"n\">kwargs</span><span class=\"p\">[</span><span class=\"s2\">&quot;max_length&quot;</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"mi\">104</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>我们的 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 接收大多数标准字段选项（参考下面的列表），但是我们确定参数是定长的，因为它只需要保存 52 个卡片和它们的值；总计 104 个字符。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>许多 Django 模型字段可以接受并没有什么用的可选参数。比如，你可以同时将 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField.auto_now\" title=\"django.db.models.DateField.auto_now\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_now</span></code></a> 和 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField.auto_now\" title=\"django.db.models.DateField.auto_now\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_now</span></code></a> 传递给 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField\" title=\"django.db.models.DateField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.db.models.DateField</span></code></a> ，它将无视参数 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.editable\" title=\"django.db.models.Field.editable\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">editable</span></code></a> （<a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField.auto_now\" title=\"django.db.models.DateField.auto_now\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_now</span></code></a> 被设置了就意味着 <code class=\"docutils literal notranslate\"><span class=\"pre\">editable=False</span></code>）。这种情况下不会抛出错误。</p>\n<p>这种行为简化了字段类，因为它不需要检查那些没必要出现的可选参数。它们传递所有可选参数到父类，之后就不再使用它们了。您可以更严格地设置字段可选参数，或者对当前字段设置更放任的行为，一切都有您来决定。</p>\n</aside>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">Field.__init__()</span></code> 方法接收以下参数:</p>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.verbose_name\" title=\"django.db.models.Field.verbose_name\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">verbose_name</span></code></a></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.primary_key\" title=\"django.db.models.Field.primary_key\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">primary_key</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.CharField.max_length\" title=\"django.db.models.CharField.max_length\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">max_length</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.unique\" title=\"django.db.models.Field.unique\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">unique</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.blank\" title=\"django.db.models.Field.blank\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">blank</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.null\" title=\"django.db.models.Field.null\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">null</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_index\" title=\"django.db.models.Field.db_index\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">db_index</span></code></a></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">rel</span></code>: 用于关联字段（像是： <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.ForeignKey\" title=\"django.db.models.ForeignKey\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ForeignKey</span></code></a>）。仅用于进阶用途。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.default\" title=\"django.db.models.Field.default\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">default</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.editable\" title=\"django.db.models.Field.editable\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">editable</span></code></a></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">serialize</span></code>: 若为 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code>，字段传给 Django 的 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/serialization/\"><span class=\"doc\">序列化器</span></a> 时不会被序列化。默认为 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.unique_for_date\" title=\"django.db.models.Field.unique_for_date\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">unique_for_date</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.unique_for_month\" title=\"django.db.models.Field.unique_for_month\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">unique_for_month</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.unique_for_year\" title=\"django.db.models.Field.unique_for_year\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">unique_for_year</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.choices\" title=\"django.db.models.Field.choices\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">choices</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.help_text\" title=\"django.db.models.Field.help_text\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">help_text</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_column\" title=\"django.db.models.Field.db_column\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">db_column</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_tablespace\" title=\"django.db.models.Field.db_tablespace\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">db_tablespace</span></code></a>: 仅为创建索引，如果后端支持 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/db/tablespaces/\"><span class=\"doc\">tablespaces</span></a>。一般情况下你可以忽略此选项。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.auto_created\" title=\"django.db.models.Field.auto_created\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_created</span></code></a>:若字段是自动创建的，则为 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>，用于 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.OneToOneField\" title=\"django.db.models.OneToOneField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">OneToOneField</span></code></a> 的模型继承。仅用于进阶用途。</p></li>\n</ul>\n<p>上述列表中所有无解释的选项与在普通 Django 字段中的作用一样。参见 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/\"><span class=\"doc\">字段文档</span></a> 获取例子和细节信息。</p>\n<section id=\"field-deconstruction\">\n<span id=\"custom-field-deconstruct-method\"></span><h3>字段解析<a class=\"heading-anchor\" href=\"#field-deconstruction\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>编写 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 方法的对应部分是编写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.deconstruct\" title=\"django.db.models.Field.deconstruct\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code></a> 方法。在 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/migrations/\"><span class=\"doc\">模型迁移</span></a> 过程中，它用于告诉 Django 如何将新字段的实例减少为序列化形式 - 特别是传递给 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 以重新创建它的哪些参数。</p>\n<p>如果你未在继承的字段之前添加任何选项，就不需要编写新的 <code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code> 方法。然而，如果你正在修改传递给 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 的参数（像 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 中的一样），你需要增补被传递的值。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code> 返回包含 4 个项目的元组：字段的属性名，字段类的完整导入路径，位置参数（以列表的形式），和关键字参数（以字典的形式）。注意，这与 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/migrations/#custom-deconstruct-method\"><span class=\"std std-ref\">为自定义类</span></a> 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code> 方法不同，它返回包含 3个项目的元组。</p>\n<p>作为自定义字段的作者，你不需要担心前两个值；基类 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 已包含处理字段属性名和导入路径的代码。然后，你仍必须关注位置参数和关键字参数，这些是你最有可能改的东西。</p>\n<p>例如，在 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 类中，我们总是强制设置 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 的长度。基类 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code> 方法会看到这个值，并尝试在关键字参数中返回它；因此，我们能为了可读性从关键字参数中剔除它:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">models</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n        <span class=\"n\">kwargs</span><span class=\"p\">[</span><span class=\"s2\">&quot;max_length&quot;</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"mi\">104</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">deconstruct</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">path</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"n\">kwargs</span> <span class=\"o\">=</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">deconstruct</span><span class=\"p\">()</span>\n        <span class=\"k\">del</span> <span class=\"n\">kwargs</span><span class=\"p\">[</span><span class=\"s2\">&quot;max_length&quot;</span><span class=\"p\">]</span>\n        <span class=\"k\">return</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">path</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"n\">kwargs</span>\n</code></pre></div>\n<p>若你添加了一个新的关键字参数，你需要在 <code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct</span></code> 中新增代码，将其值传入 <code class=\"docutils literal notranslate\"><span class=\"pre\">kwargs</span></code>。如果不需要字段的重构状态，比如使用默认值的情况，还应该忽略 <code class=\"docutils literal notranslate\"><span class=\"pre\">kwargs</span></code> 中的值。</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">models</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CommaSepField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"s2\">&quot;Implements comma-separated storage of lists&quot;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">separator</span><span class=\"o\">=</span><span class=\"s2\">&quot;,&quot;</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">separator</span> <span class=\"o\">=</span> <span class=\"n\">separator</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">deconstruct</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">path</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"n\">kwargs</span> <span class=\"o\">=</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">deconstruct</span><span class=\"p\">()</span>\n        <span class=\"c1\"># Only include kwarg if it&#39;s not the default</span>\n        <span class=\"k\">if</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">separator</span> <span class=\"o\">!=</span> <span class=\"s2\">&quot;,&quot;</span><span class=\"p\">:</span>\n            <span class=\"n\">kwargs</span><span class=\"p\">[</span><span class=\"s2\">&quot;separator&quot;</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">separator</span>\n        <span class=\"k\">return</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">path</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"n\">kwargs</span>\n</code></pre></div>\n<p>更多的复杂例子超出本文的范围，但是请牢记——对于你的字段实例的任意配置，<code class=\"docutils literal notranslate\"><span class=\"pre\">deconstruct()</span></code> 必须返回能传递给 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__</span></code> 的参数重构状态。</p>\n<p>如果你在父类 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 中设置了新的默认值需要额外注意；说明你希望总是包含它们，而不是在它们采用旧有值时消失。</p>\n<p>另外，应当避免用位置参数来返回值，尽可能用关键字参数来返回值以保证在未来能拥有最大的兼容性。如果您在构造方法参数列表中修改参数名称比修改其位置更频繁，您可能会更喜欢位置参数，但请记住，在相当长的一段时间内（也许几年），人们将从序列化版本中重新构建您的字段，这取决于您迁移持续的时间。</p>\n<p>您可以在包含字段的迁移文件里查看解析的结果，此外您可以在单元测试中通过解构和重构字段来测试。</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">path</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"n\">kwargs</span> <span class=\"o\">=</span> <span class=\"n\">my_field_instance</span><span class=\"o\">.</span><span class=\"n\">deconstruct</span><span class=\"p\">()</span>\n<span class=\"n\">new_instance</span> <span class=\"o\">=</span> <span class=\"n\">MyField</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n<span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">my_field_instance</span><span class=\"o\">.</span><span class=\"n\">some_attribute</span><span class=\"p\">,</span> <span class=\"n\">new_instance</span><span class=\"o\">.</span><span class=\"n\">some_attribute</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"field-attributes-not-affecting-database-column-definition\">\n<span id=\"custom-field-non-db-attrs\"></span><h3>字段属性不影响数据库列定义<a class=\"heading-anchor\" href=\"#field-attributes-not-affecting-database-column-definition\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你可以重写 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field.non_db_attrs</span></code> 来自定义不影响列定义的字段属性。它在模型迁移过程中用于检测无操作的 <code class=\"docutils literal notranslate\"><span class=\"pre\">AlterField</span></code> 操作。</p>\n<p>例子:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CommaSepField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"nd\">@property</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">non_db_attrs</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">non_db_attrs</span> <span class=\"o\">+</span> <span class=\"p\">(</span><span class=\"s2\">&quot;separator&quot;</span><span class=\"p\">,)</span>\n</code></pre></div>\n</section>\n<section id=\"changing-a-custom-field-s-base-class\">\n<h3>修改自定义字段的基类<a class=\"heading-anchor\" href=\"#changing-a-custom-field-s-base-class\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你可能修改自定义字段的基类，因为 Django 无法检测到修改，并为其实施迁移。例如，如果你先这样:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CustomCharField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n</code></pre></div>\n<p>随后决定继承自 <code class=\"docutils literal notranslate\"><span class=\"pre\">TextField</span></code>，你不能像这样修改子类:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CustomCharField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">TextField</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n</code></pre></div>\n<p>替代方法是，你必须新建一个自定义字段类，并将你的模型指向此类:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CustomCharField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CustomTextField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">TextField</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n</code></pre></div>\n<p>就像文档 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/migrations/#migrations-removing-model-fields\"><span class=\"std std-ref\">移除字段</span></a> 中讨论的一样，你必须保留原 <code class=\"docutils literal notranslate\"><span class=\"pre\">CustomCharField</span></code> 类只要你还有迁移指向它。</p>\n</section>\n<section id=\"documenting-your-custom-field\">\n<h3>为自定义字段编写文档<a class=\"heading-anchor\" href=\"#documenting-your-custom-field\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>像之前一样，你需要为自定义字段类型编写文档，这样用户就会知道这他喵到底是啥。除了为其提供对开发者很有用的 docstring 外，你也需要让后台管理员应用程序的用户通过应用程序 <span class=\"xref std std-doc\">django.contrib.admindocs 1</span> 看到一个关于字段类型的简单介绍。只需在自定义字段的 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.description\" title=\"django.db.models.Field.description\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">description</span></code></a> 属性提供描述性文本。在上述例子中，由 <code class=\"docutils literal notranslate\"><span class=\"pre\">admindocs</span></code> 应用为 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 字段提供的描述是 'A hand of cards (bridge style)'。</p>\n<p>在 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/contrib/admin/admindocs/#module-django.contrib.admindocs\" title=\"django.contrib.admindocs: Django's admin documentation generator.\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">django.contrib.admindocs</span></code></a> 展示的内容中，字段描述在 <code class=\"docutils literal notranslate\"><span class=\"pre\">field.__dict__</span></code> 中差值，它允许描述包含字段参数。例如， <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.CharField\" title=\"django.db.models.CharField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">CharField</span></code></a> 的说明是:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">description</span> <span class=\"o\">=</span> <span class=\"n\">_</span><span class=\"p\">(</span><span class=\"s2\">&quot;String (up to </span><span class=\"si\">%(max_length)s</span><span class=\"s2\">)&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"useful-methods\">\n<h3>实用方法<a class=\"heading-anchor\" href=\"#useful-methods\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>一旦你已创建了 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 的子类，你可能会考虑重写一些标准方法，这取决于你的字段行为。以下列表中的方法大致按重要性降序排列，即从上至下。</p>\n<section id=\"custom-database-types\">\n<span id=\"id1\"></span><h4>自定义数据库类型<a class=\"heading-anchor\" href=\"#custom-database-types\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>假设你已创建了一个 PostgreSQL 自定义字段，名叫 <code class=\"docutils literal notranslate\"><span class=\"pre\">mytype</span></code>。你可以继承 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 并实现 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 方法，像这样:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">models</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MytypeField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;mytype&quot;</span>\n</code></pre></div>\n<p>只要已建立 <code class=\"docutils literal notranslate\"><span class=\"pre\">MytypeField</span></code>，你就能像使用其它 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field</span></code> 类型一样在模型中使用它:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Person</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">(</span><span class=\"n\">max_length</span><span class=\"o\">=</span><span class=\"mi\">80</span><span class=\"p\">)</span>\n    <span class=\"n\">something_else</span> <span class=\"o\">=</span> <span class=\"n\">MytypeField</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>如果您的目标是构建一个与数据库无关的应用程序，您应该考虑数据库账户列类型的差异。 例如，PostgreSQL 中的日期/时间列类型称为 <code class=\"docutils literal notranslate\"><span class=\"pre\">timestamp</span></code> ，而 MySQL 中的同一列称为 <code class=\"docutils literal notranslate\"><span class=\"pre\">datetime</span></code> 。您可以通过 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 方法中处理此问题检查连接 <code class=\"docutils literal notranslate\"><span class=\"pre\">connection.vendor</span></code> 属性。 当前的内置供应商名称是：<code class=\"docutils literal notranslate\"><span class=\"pre\">sqlite</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">postgresql</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">mysql</span></code>, 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">oracle</span></code>。</p>\n<p>例子:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyDateField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"n\">connection</span><span class=\"o\">.</span><span class=\"n\">vendor</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;mysql&quot;</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"s2\">&quot;datetime&quot;</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"s2\">&quot;timestamp&quot;</span>\n</code></pre></div>\n<p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 和 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.rel_db_type\" title=\"django.db.models.Field.rel_db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">rel_db_type()</span></code></a> 方法在 Django 构建应用程序的 <code class=\"docutils literal notranslate\"><span class=\"pre\">CREATE</span> <span class=\"pre\">TABLE</span></code> 语句时被调用，也就是当你首次创建表时。这些方法还在构建包含模型字段的 <code class=\"docutils literal notranslate\"><span class=\"pre\">WHERE</span></code> 子句时被调用，也就是当你使用 QuerySet 方法如 <code class=\"docutils literal notranslate\"><span class=\"pre\">get()</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">filter()</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">exclude()</span></code> 来检索数据，并将模型字段作为参数时。</p>\n<p>某些数据库列类型接受参数，例如 <code class=\"docutils literal notranslate\"><span class=\"pre\">CHAR(25)</span></code>，参数 <code class=\"docutils literal notranslate\"><span class=\"pre\">25</span></code> 表示列的最大长度。类似用例中，该参数若在模型中指定比硬编码在 <code class=\"docutils literal notranslate\"><span class=\"pre\">db_type()</span></code> 方法中更灵活。举个例子，构建 <code class=\"docutils literal notranslate\"><span class=\"pre\">CharMaxlength25Field</span></code> 没多大意义，如下所示:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># This is a silly example of hard-coded parameters.</span>\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">CharMaxlength25Field</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;char(25)&quot;</span>\n\n\n<span class=\"c1\"># In the model:</span>\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyModel</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n    <span class=\"n\">my_field</span> <span class=\"o\">=</span> <span class=\"n\">CharMaxlength25Field</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>更好的方式是在运行时指定参数值——即类实例化的时候。需要像这样实现 <code class=\"docutils literal notranslate\"><span class=\"pre\">Field.__init__()</span></code> 即可:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># This is a much more flexible example.</span>\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">BetterCharField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">max_length</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">max_length</span> <span class=\"o\">=</span> <span class=\"n\">max_length</span>\n        <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;char(</span><span class=\"si\">%s</span><span class=\"s2\">)&quot;</span> <span class=\"o\">%</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">max_length</span>\n\n\n<span class=\"c1\"># In the model:</span>\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyModel</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n    <span class=\"n\">my_field</span> <span class=\"o\">=</span> <span class=\"n\">BetterCharField</span><span class=\"p\">(</span><span class=\"mi\">25</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>最后，如果你的列真的要求配置复杂的 SQL，从 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>。这会让 Django 创建 SQL 的代码跳过该字段。随后你需要负责为该字段在正确的表中以某种方式创建列，这种方式允许你告诉 Django 不处理此事。</p>\n<p><a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.rel_db_type\" title=\"django.db.models.Field.rel_db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">rel_db_type()</span></code></a> 方法由字段调用，例如 <code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignKey</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">OneToOneField</span></code> ，这些通过指向另一个字段来决定数据库列类型的字段。举个例子，如果你有个 <code class=\"docutils literal notranslate\"><span class=\"pre\">UnsignedAutoField</span></code>，你也需要指向该字段的外键使用相同的数据类型:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># MySQL unsigned integer (range 0 to 4294967295).</span>\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">UnsignedAutoField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">AutoField</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;integer UNSIGNED AUTO_INCREMENT&quot;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">rel_db_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;integer UNSIGNED&quot;</span>\n</code></pre></div>\n</section>\n<section id=\"converting-values-to-python-objects\">\n<span id=\"id2\"></span><h4>将值转为 Python 对象<a class=\"heading-anchor\" href=\"#converting-values-to-python-objects\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>若自定义 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field\" title=\"django.db.models.Field\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Field</span></code></a> 处理的数据结构比字符串，日期，整型，或浮点型更复杂，你可能需要重写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.from_db_value\" title=\"django.db.models.Field.from_db_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">from_db_value()</span></code></a> 和 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.to_python\" title=\"django.db.models.Field.to_python\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">to_python()</span></code></a>。</p>\n<p>若要展示字段的子类， <code class=\"docutils literal notranslate\"><span class=\"pre\">from_db_value()</span></code> 将会在从数据库中载入的生命周期中调用，包括聚集和 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/querysets/#django.db.models.query.QuerySet.values\" title=\"django.db.models.query.QuerySet.values\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">values()</span></code></a> 调用。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">to_python()</span></code> 在反序列化时和为表单应用 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/instances/#django.db.models.Model.clean\" title=\"django.db.models.Model.clean\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">clean()</span></code></a> 时调用。</p>\n<p>作为通用规则， <code class=\"docutils literal notranslate\"><span class=\"pre\">to_python</span></code> 应该平滑地处理以下参数：</p>\n<ul class=\"simple\">\n<li><p>一个正确的类型（本业持续介绍的例子 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> ）。</p></li>\n<li><p>一个字符串</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> （若字段允许 <code class=\"docutils literal notranslate\"><span class=\"pre\">null=True</span></code>）</p></li>\n</ul>\n<p>在我们的 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 类中，我们将数据存储为数据库中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">VARCHAR</span></code> 字段，因此需要能够处理字符串和 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> 在 <code class=\"docutils literal notranslate\"><span class=\"pre\">from_db_value()</span></code> 中。在 <code class=\"docutils literal notranslate\"><span class=\"pre\">to_python()</span></code> 中，我们还需要处理 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 实例：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">re</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.exceptions</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">ValidationError</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">models</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils.translation</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">gettext_lazy</span> <span class=\"k\">as</span> <span class=\"n\">_</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">parse_hand</span><span class=\"p\">(</span><span class=\"n\">hand_string</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;Takes a string of cards and splits into a full hand.&quot;&quot;&quot;</span>\n    <span class=\"n\">p1</span> <span class=\"o\">=</span> <span class=\"n\">re</span><span class=\"o\">.</span><span class=\"n\">compile</span><span class=\"p\">(</span><span class=\"s2\">&quot;.</span><span class=\"si\">{26}</span><span class=\"s2\">&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">p2</span> <span class=\"o\">=</span> <span class=\"n\">re</span><span class=\"o\">.</span><span class=\"n\">compile</span><span class=\"p\">(</span><span class=\"s2\">&quot;..&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">args</span> <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"n\">p2</span><span class=\"o\">.</span><span class=\"n\">findall</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">)</span> <span class=\"k\">for</span> <span class=\"n\">x</span> <span class=\"ow\">in</span> <span class=\"n\">p1</span><span class=\"o\">.</span><span class=\"n\">findall</span><span class=\"p\">(</span><span class=\"n\">hand_string</span><span class=\"p\">)]</span>\n    <span class=\"k\">if</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">args</span><span class=\"p\">)</span> <span class=\"o\">!=</span> <span class=\"mi\">4</span><span class=\"p\">:</span>\n        <span class=\"k\">raise</span> <span class=\"n\">ValidationError</span><span class=\"p\">(</span><span class=\"n\">_</span><span class=\"p\">(</span><span class=\"s2\">&quot;Invalid input for a Hand instance&quot;</span><span class=\"p\">))</span>\n    <span class=\"k\">return</span> <span class=\"n\">Hand</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">args</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">from_db_value</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">,</span> <span class=\"n\">expression</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"n\">value</span> <span class=\"ow\">is</span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"n\">value</span>\n        <span class=\"k\">return</span> <span class=\"n\">parse_hand</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">to_python</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"nb\">isinstance</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span> <span class=\"n\">Hand</span><span class=\"p\">):</span>\n            <span class=\"k\">return</span> <span class=\"n\">value</span>\n\n        <span class=\"k\">if</span> <span class=\"n\">value</span> <span class=\"ow\">is</span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"n\">value</span>\n\n        <span class=\"k\">return</span> <span class=\"n\">parse_hand</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>注意，我们总是为这些方法返回一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 实例。这就是我们要保存在模型属性中的 Python 对象类型。</p>\n<p>对于 <code class=\"docutils literal notranslate\"><span class=\"pre\">to_python()</span></code> 来说，如果在值转换过程中出现任何问题，你应该抛出一个 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code></a> 异常。</p>\n</section>\n<section id=\"converting-python-objects-to-query-values\">\n<span id=\"id3\"></span><h4>将 Python 转为查询值<a class=\"heading-anchor\" href=\"#converting-python-objects-to-query-values\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>使用数据库需要双向转换，如果你重写了 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.from_db_value\" title=\"django.db.models.Field.from_db_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">from_db_value()</span></code></a> 方法，你也必须重写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_prep_value\" title=\"django.db.models.Field.get_prep_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_prep_value()</span></code></a> 将 Python 对象转回查询值。</p>\n<p>例子:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_prep_value</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;&quot;</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span>\n            <span class=\"p\">[</span><span class=\"s2\">&quot;&quot;</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">l</span><span class=\"p\">)</span> <span class=\"k\">for</span> <span class=\"n\">l</span> <span class=\"ow\">in</span> <span class=\"p\">(</span><span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">north</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">east</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">south</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">west</span><span class=\"p\">)]</span>\n        <span class=\"p\">)</span>\n</code></pre></div>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Warning</p>\n<p>如果你使用了 MySQL 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">CHAR</span></code>，<code class=\"docutils literal notranslate\"><span class=\"pre\">VARCHAR</span></code> 或 <code class=\"docutils literal notranslate\"><span class=\"pre\">TEXT</span></code> 类型，你必须确保 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_prep_value\" title=\"django.db.models.Field.get_prep_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_prep_value()</span></code></a> 总是返回一个字符串。在 MySQL 中对这些类型操作时非常灵活，甚至有时超出预期，在传入值为正数时，检出结果可能包含非期望的结果。这个问题不会在你总为 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_prep_value\" title=\"django.db.models.Field.get_prep_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_prep_value()</span></code></a> 返回字符串类型的时候出现。</p>\n</aside>\n</section>\n<section id=\"converting-query-values-to-database-values\">\n<span id=\"id4\"></span><h4>将查询值转为数据库值<a class=\"heading-anchor\" href=\"#converting-query-values-to-database-values\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>某些数据类型（比如 dates）在数据库后端处理前要转为某种特定格式。 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_db_prep_value\" title=\"django.db.models.Field.get_db_prep_value\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_db_prep_value()</span></code></a> 实现了这种转换。查询所以使用的连接由 <code class=\"docutils literal notranslate\"><span class=\"pre\">connection</span></code> 参数指定。这允许你在需要时指定后台要求的转换逻辑。</p>\n<p>例如，Django 为其 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.BinaryField\" title=\"django.db.models.BinaryField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">BinaryField</span></code></a> 利用以下方法:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_db_prep_value</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">,</span> <span class=\"n\">prepared</span><span class=\"o\">=</span><span class=\"kc\">False</span><span class=\"p\">):</span>\n    <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">get_db_prep_value</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span> <span class=\"n\">connection</span><span class=\"p\">,</span> <span class=\"n\">prepared</span><span class=\"p\">)</span>\n    <span class=\"k\">if</span> <span class=\"n\">value</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n        <span class=\"k\">return</span> <span class=\"n\">connection</span><span class=\"o\">.</span><span class=\"n\">Database</span><span class=\"o\">.</span><span class=\"n\">Binary</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">value</span>\n</code></pre></div>\n<p>万一自定义字段需要与普通查询参数使用的转换不同的转换规则，你可以重写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_db_prep_save\" title=\"django.db.models.Field.get_db_prep_save\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_db_prep_save()</span></code></a>。</p>\n</section>\n<section id=\"preprocessing-values-before-saving\">\n<span id=\"id5\"></span><h4>在保存前预处理数值<a class=\"heading-anchor\" href=\"#preprocessing-values-before-saving\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>如果你要在保存前预处理值，你可以调用 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.pre_save\" title=\"django.db.models.Field.pre_save\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">pre_save()</span></code></a>。举个例子，Django 的 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateTimeField\" title=\"django.db.models.DateTimeField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DateTimeField</span></code></a> 在 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField.auto_now\" title=\"django.db.models.DateField.auto_now\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_now</span></code></a> 或 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.DateField.auto_now_add\" title=\"django.db.models.DateField.auto_now_add\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">auto_now_add</span></code></a> 中利用此方法正确设置属性。</p>\n<p>如果你重写了此方法，你必须在最后返回该属性的值。如果修改了值，那么你也需要更新模型属性，这样持有该引用的模型总会看到正确的值。</p>\n</section>\n<section id=\"specifying-the-form-field-for-a-model-field\">\n<span id=\"specifying-form-field-for-model-field\"></span><h4>为模型字段指定表单字段<a class=\"heading-anchor\" href=\"#specifying-the-form-field-for-a-model-field\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>为了自定义 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/forms/modelforms/#django.forms.ModelForm\" title=\"django.forms.ModelForm\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ModelForm</span></code></a> 使用的表单属性，你必须重写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.formfield\" title=\"django.db.models.Field.formfield\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">formfield()</span></code></a>。</p>\n<p>表单字段类能通过 <code class=\"docutils literal notranslate\"><span class=\"pre\">form_class</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">choices_form_class</span></code> 参数指定；如果字段指定了选项，则使用后者，反之前者。若未提供这些参数，将会使用 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/forms/fields/#django.forms.CharField\" title=\"django.forms.CharField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">CharField</span></code></a> 或 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/forms/fields/#django.forms.TypedChoiceField\" title=\"django.forms.TypedChoiceField\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">TypedChoiceField</span></code></a>。</p>\n<p>完整的 <code class=\"docutils literal notranslate\"><span class=\"pre\">kwargs</span></code> 被直接传递给表单字段的 <code class=\"docutils literal notranslate\"><span class=\"pre\">__init__()</span></code> 方法。一般的，你要做的全部工作就是为 <code class=\"docutils literal notranslate\"><span class=\"pre\">form_class</span></code> 参数配置一个合适的默认值，并在随后委托父类处理。这可能要求你编写一个自定义表单字段（甚至表单视图）。查看 <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/forms/\"><span class=\"doc\">表单文件材料</span></a> 获取相关信息。</p>\n<p>If you wish to exclude the field from the <a class=\"reference internal\" href=\"/zh-hans/5.2/topics/forms/modelforms/#django.forms.ModelForm\" title=\"django.forms.ModelForm\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ModelForm</span></code></a>, you\ncan override the <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.formfield\" title=\"django.db.models.Field.formfield\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">formfield()</span></code></a> method to return <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>.</p>\n<p>承接上面的例子，我们能这样编写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.formfield\" title=\"django.db.models.Field.formfield\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">formfield()</span></code></a> 方法:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">formfield</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n        <span class=\"c1\"># Exclude the field from the ModelForm when some condition is met.</span>\n        <span class=\"n\">some_condition</span> <span class=\"o\">=</span> <span class=\"n\">kwargs</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;some_condition&quot;</span><span class=\"p\">,</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"n\">some_condition</span><span class=\"p\">:</span>\n            <span class=\"k\">return</span> <span class=\"kc\">None</span>\n\n        <span class=\"c1\"># Set up some defaults while letting the caller override them.</span>\n        <span class=\"n\">defaults</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">&quot;form_class&quot;</span><span class=\"p\">:</span> <span class=\"n\">MyFormField</span><span class=\"p\">}</span>\n        <span class=\"n\">defaults</span><span class=\"o\">.</span><span class=\"n\">update</span><span class=\"p\">(</span><span class=\"n\">kwargs</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">formfield</span><span class=\"p\">(</span><span class=\"o\">**</span><span class=\"n\">defaults</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>这假定我们已导入 <code class=\"docutils literal notranslate\"><span class=\"pre\">MyFormField</span></code> 字段类（它有默认视图）。本页文档未覆盖编写自定义表单字段的细节。</p>\n</section>\n<section id=\"emulating-built-in-field-types\">\n<span id=\"id6\"></span><h4>仿造内置字段类型<a class=\"heading-anchor\" href=\"#emulating-built-in-field-types\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>若你已创建了 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 方法，你无需担心 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_internal_type\" title=\"django.db.models.Field.get_internal_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_internal_type()</span></code></a> 方法——它并不常用。虽然很多时候，数据库存储行为和其他字段类似，所以你能直接用其它字段的逻辑创建正确的列。</p>\n<p>例子:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_internal_type</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"s2\">&quot;CharField&quot;</span>\n</code></pre></div>\n<p>无论我们使用了哪个数据库后端， <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/django-admin/#django-admin-migrate\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">migrate</span></code></a> 或其它 SQL 命令总会在保存字符串时为其创建正确的列类型。</p>\n<p>若 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.get_internal_type\" title=\"django.db.models.Field.get_internal_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get_internal_type()</span></code></a> 返回了当前数据库后端（即 <code class=\"docutils literal notranslate\"><span class=\"pre\">django.db.backends.&lt;db_name&gt;.base.DatabaseWrapper.data_types</span></code> 中未出现的后端）无法理解的字符串——该字符串仍会被序列化器使用的，但是默认的 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 方法会返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>。查阅文档 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.db_type\" title=\"django.db.models.Field.db_type\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">db_type()</span></code></a> 了解为啥有用。如果您打算在 Django 之外的其他地方使用序列化器输出，那么将描述性字符串作为序列化器的字段类型是一个有用的想法。</p>\n</section>\n<section id=\"converting-field-data-for-serialization\">\n<span id=\"converting-model-field-to-serialization\"></span><h4>为序列化转换字段数据<a class=\"heading-anchor\" href=\"#converting-field-data-for-serialization\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>自定义序列化器序列化值的流程，你要重写 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.value_to_string\" title=\"django.db.models.Field.value_to_string\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">value_to_string()</span></code></a>。使用 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/models/fields/#django.db.models.Field.value_to_string\" title=\"django.db.models.Field.value_to_string\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">value_to_string()</span></code></a> 是在序列化之前获取字段值的最佳方法。举个例子，由于 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code> 使用字符串存储数据，我们能复用一些已有代码:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">HandField</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Field</span><span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">value_to_string</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">obj</span><span class=\"p\">):</span>\n        <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">value_from_object</span><span class=\"p\">(</span><span class=\"n\">obj</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">get_prep_value</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n</section>\n<section id=\"some-general-advice\">\n<h3>一些通用建议<a class=\"heading-anchor\" href=\"#some-general-advice\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>编写自定义字段是个棘手的，尤其是在 Python 类，数据库，序列化格式之间进行复杂转换的时候。下面有几个让事情更顺利的建议：</p>\n<ol class=\"arabic simple\">\n<li><p>查看现有的 Django 字段（在 <a class=\"extlink-source reference external\" href=\"https://github.com/django/django/blob/stable/5.2.x/django/db/models/fields/__init__.py\">django/db/models/fields/__init__.py</a> 中）以获取灵感。尝试找到与你想要的类似的字段，并稍微扩展它，而不是从头开始创建一个全新的字段。</p></li>\n<li><p>为字段类添加一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">__str__()</span></code> 方法。在很多地方，字段代码的默认行为是对值调用 <code class=\"docutils literal notranslate\"><span class=\"pre\">str()</span></code>。（本页文档中， <code class=\"docutils literal notranslate\"><span class=\"pre\">value</span></code> 会是一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">Hand</span></code> 实例，而不是 <code class=\"docutils literal notranslate\"><span class=\"pre\">HandField</span></code>）。所以 <code class=\"docutils literal notranslate\"><span class=\"pre\">__str()__</span></code> 方法会自动将 Python 对象转为字符串格式，帮你剩下不少时间。</p></li>\n</ol>\n</section>\n</section>\n<section id=\"writing-a-filefield-subclass\">\n<h2>编写一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">FileField</span></code> 子类<a class=\"heading-anchor\" href=\"#writing-a-filefield-subclass\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>除了上述方法外，处理文件的字段还有一些必须考虑到的特殊要求。 <code class=\"docutils literal notranslate\"><span class=\"pre\">FileField</span></code> 提供的大部分机制（像是操作数据库存储和检索）能保持不变，让子类面对支持特殊文件的挑战。</p>\n<p>Django 提供一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code> 类，作为文件内容和文件操作的代理。可以继承该类自定义访问文件的方式，哪些方法是可用的。它位于 <code class=\"docutils literal notranslate\"><span class=\"pre\">django.db.models.fields.files</span></code>，它的默认行为在 <a class=\"reference internal\" href=\"/zh-hans/5.2/ref/files/file/\"><span class=\"doc\">file 文档</span></a> 中介绍。</p>\n<p>一旦创建了文件 <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code> 子类，必须说明要使用新子类 <code class=\"docutils literal notranslate\"><span class=\"pre\">FileField</span></code>。为此，需要为 <code class=\"docutils literal notranslate\"><span class=\"pre\">FileField</span></code> 的子类的 <code class=\"docutils literal notranslate\"><span class=\"pre\">attr_class</span></code> 属性指定新的文件 <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code> 子类。</p>\n<section id=\"a-few-suggestions\">\n<h3>一些建议<a class=\"heading-anchor\" href=\"#a-few-suggestions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>除了上述细节，下面还有一些准则，有助于极大地提高字段代码的效率和可读性。</p>\n<ol class=\"arabic simple\">\n<li><p>Django 自带的 <code class=\"docutils literal notranslate\"><span class=\"pre\">ImageField</span></code> 的源代码（在 <a class=\"extlink-source reference external\" href=\"https://github.com/django/django/blob/stable/5.2.x/django/db/models/fields/files.py\">django/db/models/fields/files.py</a> 中）是一个很好的示例，展示了如何通过子类化 <code class=\"docutils literal notranslate\"><span class=\"pre\">FileField</span></code> 来支持特定类型的文件，因为它包含了上面描述的所有技术。</p></li>\n<li><p>尽可能的缓存文件属性。因为文件可能保存在远端存储系统中，检出它们会消耗额外的时间，甚至是钱，且不总是必要的。一旦检出某个文件，获取其内容，尽可能缓存所有数据，以减少后续调用再次检索文件的次数。</p></li>\n</ol>\n</section>\n</section>","rootId":"how-to-create-custom-model-fields","toc":[{"title":"介绍","anchor":"introduction","children":[{"title":"我们的示例对象","anchor":"our-example-object","children":[]}]},{"title":"背后的理论","anchor":"background-theory","children":[{"title":"数据库存储","anchor":"database-storage","children":[]},{"title":"一个字段(Field)类做了什么？","anchor":"what-does-a-field-class-do","children":[]}]},{"title":"编写一个 field 子类","anchor":"writing-a-field-subclass","children":[{"title":"字段解析","anchor":"field-deconstruction","children":[]},{"title":"字段属性不影响数据库列定义","anchor":"field-attributes-not-affecting-database-column-definition","children":[]},{"title":"修改自定义字段的基类","anchor":"changing-a-custom-field-s-base-class","children":[]},{"title":"为自定义字段编写文档","anchor":"documenting-your-custom-field","children":[]},{"title":"实用方法","anchor":"useful-methods","children":[{"title":"自定义数据库类型","anchor":"custom-database-types","children":[]},{"title":"将值转为 Python 对象","anchor":"converting-values-to-python-objects","children":[]},{"title":"将 Python 转为查询值","anchor":"converting-python-objects-to-query-values","children":[]},{"title":"将查询值转为数据库值","anchor":"converting-query-values-to-database-values","children":[]},{"title":"在保存前预处理数值","anchor":"preprocessing-values-before-saving","children":[]},{"title":"为模型字段指定表单字段","anchor":"specifying-the-form-field-for-a-model-field","children":[]},{"title":"仿造内置字段类型","anchor":"emulating-built-in-field-types","children":[]},{"title":"为序列化转换字段数据","anchor":"converting-field-data-for-serialization","children":[]}]},{"title":"一些通用建议","anchor":"some-general-advice","children":[]}]},{"title":"编写一个 FileField 子类","anchor":"writing-a-filefield-subclass","children":[{"title":"一些建议","anchor":"a-few-suggestions","children":[]}]}],"breadcrumbs":[{"docname":"howto/index","title":"如何引导","url":"/zh-hans/5.2/howto/"}],"prev":{"docname":"howto/legacy-databases","title":"如何将 Django 与旧有的数据库集成","url":"/zh-hans/5.2/howto/legacy-databases/"},"next":{"docname":"howto/writing-migrations","title":"如何创建数据库迁移","url":"/zh-hans/5.2/howto/writing-migrations/"},"formats":{"html":"/zh-hans/5.2/howto/custom-model-fields/","markdown":"/zh-hans/5.2/howto/custom-model-fields.md","json":"/zh-hans/5.2/howto/custom-model-fields.json"},"source":"https://github.com/django/django/blob/stable/5.2.x/docs/howto/custom-model-fields.txt","official":"https://docs.djangoproject.com/zh-hans/5.2/howto/custom-model-fields/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}