{"title":"自定义查找","version":"2.1","locale":"zh-hans","docname":"howto/custom-lookups","url":"/zh-hans/2.1/howto/custom-lookups/","canonical":"https://djangodocs.dev/zh-hans/2.1/howto/custom-lookups/","summary":"Django提供了各种各样的ref：用于过滤的内置查找（例如，“exact”和“icontains”）。本文档解释了如何编写自定义查找以及如何更改已有查找的工作方式。","html":"<h1>自定义查找<a class=\"heading-anchor\" href=\"#custom-lookups\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Django提供了各种各样的ref：用于过滤的内置查找（例如，“exact”和“icontains”）。 本文档解释了如何编写自定义查找以及如何更改已有查找的工作方式。 有关lookup的API参考，请参阅：doc：/ ref / models / lookups。</p>\n<section id=\"a-simple-lookup-example\">\n<h2>一个简单的查找示例<a class=\"heading-anchor\" href=\"#a-simple-lookup-example\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>让我们从一个简单的自定义查找开始。我们编写一个自定义查找 ne ，它与 exact 相反。\nAuthor.objects.filter(name__ne='Jack') 将会转换成 SQL:</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=\"s2\">&quot;author&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;name&quot;</span> <span class=\"o\">&lt;&gt;</span> <span class=\"s1\">&#39;Jack&#39;</span>\n</code></pre></div>\n<p>SQL 会自动适配不同的后端, 所以我们不需要对使用不同的数据库担心.</p>\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=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Lookup\n\n<span class=\"k\">class</span> <span class=\"nc\">NotEqual</span><span class=\"p\">(</span>Lookup<span class=\"p\">):</span>\n    lookup_name <span class=\"o\">=</span> <span class=\"s1\">&#39;ne&#39;</span>\n\n    <span class=\"k\">def</span> <span class=\"nf\">as_sql</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> compiler<span class=\"p\">,</span> connection<span class=\"p\">):</span>\n        lhs<span class=\"p\">,</span> lhs_params <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span>process_lhs<span class=\"p\">(</span>compiler<span class=\"p\">,</span> connection<span class=\"p\">)</span>\n        rhs<span class=\"p\">,</span> rhs_params <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span>process_rhs<span class=\"p\">(</span>compiler<span class=\"p\">,</span> connection<span class=\"p\">)</span>\n        params <span class=\"o\">=</span> lhs_params <span class=\"o\">+</span> rhs_params\n        <span class=\"k\">return</span> <span class=\"s1\">&#39;</span><span class=\"si\">%s</span><span class=\"s1\"> &lt;&gt; </span><span class=\"si\">%s</span><span class=\"s1\">&#39;</span> <span class=\"o\">%</span> <span class=\"p\">(</span>lhs<span class=\"p\">,</span> rhs<span class=\"p\">),</span> params\n</code></pre></div>\n<p>要注册``NotEqual``查找，我们只需要在我们希望查找可用的字段类上调用``register_lookup``方法。 在这种情况下，查找对所有``Field``子类都有意义，所以我们直接用``Field``注册它:</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=\"nn\">django.db.models.fields</span> <span class=\"kn\">import</span> Field\nField<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>NotEqual<span class=\"p\">)</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=\"kn\">from</span> <span class=\"nn\">django.db.models.fields</span> <span class=\"kn\">import</span> Field\n\n<span class=\"nd\">@Field</span><span class=\"o\">.</span>register_lookup\n<span class=\"k\">class</span> <span class=\"nc\">NotEqualLookup</span><span class=\"p\">(</span>Lookup<span class=\"p\">):</span>\n    <span class=\"c1\"># ...</span>\n</code></pre></div>\n<p>现在我们可以用“foo__ne”来代表“foo”的任意字段。你需要确保在创建任意的queryset之前使用它。你可以在“models.py”文件内设置它，或者在“AppConfig”内使用“ready()”方法注册它。</p>\n<p>仔细观察实现过程，最开始我们需要“lookup_name”这个属性。这个可以保证ORM理解如何编译“name_ne”和使用“NotEqual”来建立结构化查询语言SQL。按照惯例，这些名字“name_ne”是小写字母字符串，但是很麻烦的是必须有“__”字符串</p>\n<p>之后我们需要定义一个“as_sql”方法。这方法需要一个“SQLCompiler” 对象, 被叫做编译器，和一个有效的数据库连接。“SQLCompller”对象没有文档，我们只需要知道它有一个compile()方法可以返回一个元组包括SQL字符串，和插入这个字符串的参数。大部分情况下，你不需要直接使用这个对象你可以把它传送给“process_lhs()”和“process_rhs()”</p>\n<p>“Lookup”工作依靠两个值, “lhs”和“rhs”,代表左右手边，左手是一个字段参考，但它可以是任何来解释ref：应用程序的检索表达。右手是一个用户给的数值。举个例子：<code class=\"docutils literal notranslate\">Author.objects.filter(name__ne='Jack')</code>，左手是一个参考对应着Author模型的名字，“Jack”是右手端。</p>\n<p>我们调用“process_lhs”和“process_rhs”转化他们成为我们想要的用来检索的值通过之前我们提到的“编译器”。这个方法返回一个元组包含SQL数据库和插入SQL数据库一些参数，刚好就是我们‘as_sql’需要返回的。使用前面的例子，“process_lhs”返回``('&quot;author&quot;.&quot;name&quot;', [])`` ，“process_lhs”返回``('&quot;%s&quot;', ['Jack'])``.在这个例子里面没有左手边的参数，但是这需要看情况而定，我们还需要包括这些参数当我们返回的时候。</p>\n<p>最后，我们将这些部分组合成一个带有``&lt;&gt;``的SQL表达式，并提供查询的所有参数。 然后我们返回一个包含生成的SQL字符串和参数的元组。</p>\n</section>\n<section id=\"a-simple-transformer-example\">\n<h2>让我们举一个简单的转换器示例<a class=\"heading-anchor\" href=\"#a-simple-transformer-example\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>上面的自定义查找没问题，但在某些情况下，您可能希望能够将一些查找链接在一起。 例如，假设我们正在构建一个我们想要制作一个带有``abs（）``运算符的应用程序。 我们有一个``Experiment``模型，它记录起始值，结束值和变化（开始 - 结束）。 我们想找到所有在Experiment模型中change属性等于一定数量的（<code class=\"docutils literal notranslate\">Experiment.objects.filter（change__abs = 27）</code>），或者在Experiment模型中change属性没有超过一定数量的（<code class=\"docutils literal notranslate\">Experiment.objects.filter（change__abs__lt= 27）</code>）。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>这个例子有点刻意，但它很好地演示了以数据库后端独立方式可能实现的功能范围，并且没有重复Django中的功能</p>\n</aside>\n<p>我们将从编写一个``AbsoluteValue``变换器开始。 这将使用SQL中的``ABS（）``函数在比较进行之前首先转换值:</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=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Transform\n\n<span class=\"k\">class</span> <span class=\"nc\">AbsoluteValue</span><span class=\"p\">(</span>Transform<span class=\"p\">):</span>\n    lookup_name <span class=\"o\">=</span> <span class=\"s1\">&#39;abs&#39;</span>\n    function <span class=\"o\">=</span> <span class=\"s1\">&#39;ABS&#39;</span>\n</code></pre></div>\n<p>下一步, 让我们为其注册 <code class=\"docutils literal notranslate\">IntrgerField</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=\"nn\">django.db.models</span> <span class=\"kn\">import</span> IntegerField\nIntegerField<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>AbsoluteValue<span class=\"p\">)</span>\n</code></pre></div>\n<p>我们现在可以运行之前的查询。 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>Experiment.objects.filter（change__abs = 27）``将生成以下SQL</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>SELECT <span class=\"o\">...</span> WHERE ABS<span class=\"p\">(</span><span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span><span class=\"p\">)</span> <span class=\"o\">=</span> <span class=\"mi\">27</span>\n</code></pre></div>\n<p>通过使用``Transform``而不是``Lookup``，这意味着我们可以在之后链接进一步的查找。 所以``Experiment.objects.filter（change__abs__lt = 27）``将生成以下SQL</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>SELECT <span class=\"o\">...</span> WHERE ABS<span class=\"p\">(</span><span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span><span class=\"p\">)</span> <span class=\"o\">&lt;</span> <span class=\"mi\">27</span>\n</code></pre></div>\n<p>请注意，如果没有指定其他查找定义，Django则会将``change__abs = 27``解析为``change__abs__exact = 27``。</p>\n<p>这也允许结果用于``ORDER BY``和``DISTINCT ON``子句。 例如``Experiment.objects.order_by（'change__abs'）``会生成:</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>SELECT <span class=\"o\">...</span> ORDER BY ABS<span class=\"p\">(</span><span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span><span class=\"p\">)</span> ASC\n</code></pre></div>\n<p>在支持字段去重的数据库（例如PostgreSQL）上，语句``Experiment.objects.distinct（'change__abs'）``会生成:</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>SELECT <span class=\"o\">...</span> DISTINCT ON ABS<span class=\"p\">(</span><span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n<aside class=\"version-note version-changed\" data-version=\"2.1\" role=\"note\">\n<p class=\"version-note-title\">Changed in Django 2.1</p><p>上两段所提到的排序与去重的支持被加入了。</p>\n</aside>\n<p>当我们在应用``Transform``之后查找允许哪些查找执行时，Django使用``output_field``属性。 我们不需要在这里指定它，因为它没有改变，但假设我们将``AbsoluteValue``应用于某个字段，该字段表示更复杂的类型（例如，相对于原点的点或复数） 那么我们可能想要指定转换返回一个``FloatField``类型以进行进一步的查找。 这可以通过在变换中添加``output_field``属性来完成:</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=\"nn\">django.db.models</span> <span class=\"kn\">import</span> FloatField<span class=\"p\">,</span> Transform\n\n<span class=\"k\">class</span> <span class=\"nc\">AbsoluteValue</span><span class=\"p\">(</span>Transform<span class=\"p\">):</span>\n    lookup_name <span class=\"o\">=</span> <span class=\"s1\">&#39;abs&#39;</span>\n    function <span class=\"o\">=</span> <span class=\"s1\">&#39;ABS&#39;</span>\n\n    <span class=\"nd\">@property</span>\n    <span class=\"k\">def</span> <span class=\"nf\">output_field</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> FloatField<span class=\"p\">()</span>\n</code></pre></div>\n<p>这确保了像``abs__lte``这样的进一步查找与对``FloatField``一致。</p>\n</section>\n<section id=\"writing-an-efficient-abs-lt-lookup\">\n<h2>编写一个高效的 <code class=\"docutils literal notranslate\">abs__lt</code> 查找<a class=\"heading-anchor\" href=\"#writing-an-efficient-abs-lt-lookup\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>当使用上面写的``abs``查找时，生成的SQL在某些情况下不会有效地使用索引。 特别是，当我们使用``change__abs__lt = 27``时，这相当于``change__gt = -27``和``change__lt = 27``。 （对于``lte``情况，我们可以使用SQL``BETWEEN``）。</p>\n<p>因此, 我们希望 <code class=\"docutils literal notranslate\">Experiment.objects.filter(change__abs__lt=27)</code> 能生成以下 SQL:</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>SELECT <span class=\"o\">..</span> WHERE <span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span> <span class=\"o\">&lt;</span> <span class=\"mi\">27</span> AND <span class=\"s2\">&quot;experiments&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;change&quot;</span> <span class=\"o\">&gt;</span> <span class=\"o\">-</span><span class=\"mi\">27</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=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Lookup\n\n<span class=\"k\">class</span> <span class=\"nc\">AbsoluteValueLessThan</span><span class=\"p\">(</span>Lookup<span class=\"p\">):</span>\n    lookup_name <span class=\"o\">=</span> <span class=\"s1\">&#39;lt&#39;</span>\n\n    <span class=\"k\">def</span> <span class=\"nf\">as_sql</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> compiler<span class=\"p\">,</span> connection<span class=\"p\">):</span>\n        lhs<span class=\"p\">,</span> lhs_params <span class=\"o\">=</span> compiler<span class=\"o\">.</span>compile<span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span>lhs<span class=\"o\">.</span>lhs<span class=\"p\">)</span>\n        rhs<span class=\"p\">,</span> rhs_params <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span>process_rhs<span class=\"p\">(</span>compiler<span class=\"p\">,</span> connection<span class=\"p\">)</span>\n        params <span class=\"o\">=</span> lhs_params <span class=\"o\">+</span> rhs_params <span class=\"o\">+</span> lhs_params <span class=\"o\">+</span> rhs_params\n        <span class=\"k\">return</span> <span class=\"s1\">&#39;</span><span class=\"si\">%s</span><span class=\"s1\"> &lt; </span><span class=\"si\">%s</span><span class=\"s1\"> AND </span><span class=\"si\">%s</span><span class=\"s1\"> &gt; -</span><span class=\"si\">%s</span><span class=\"s1\">&#39;</span> <span class=\"o\">%</span> <span class=\"p\">(</span>lhs<span class=\"p\">,</span> rhs<span class=\"p\">,</span> lhs<span class=\"p\">,</span> rhs<span class=\"p\">),</span> params\n\nAbsoluteValue<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>AbsoluteValueLessThan<span class=\"p\">)</span>\n</code></pre></div>\n<p>这里有几件值得注意的事情。 首先，<code class=\"docutils literal notranslate\">AbsoluteValueLessThan``没有调用``process_lhs（）</code>。 相反，它会跳过由``AbsoluteValue``完成的``lhs``的转换，并使用原始的``lhs``。 也就是说，我们希望得到``&quot;experiments&quot;.&quot;change&quot;<code class=\"docutils literal notranslate\">而不是``ABS（“实验”。“改变”）</code>。 直接引用``self.lhs.lhs``是安全的，因为``AbsoluteValueLessThan``只能从``AbsoluteValue``查找访问，即``lhs``总是``AbsoluteValue`的实例`。</p>\n<p>另请注意，由于在查询中多次使用双方，所以需要多次包含“lhs_params”和“rhs_params”的参数。</p>\n<p>最后的查询直接在数据库中进行反转（ 27 到 -27 ）。 这样做的原因是，如果 self.rhs 不是普通的整数值（例如 F() 引用），我们就不能在Python中进行转换。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>In fact, most lookups with <code class=\"docutils literal notranslate\">__abs</code> could be implemented as range queries\nlike this, and on most database backends it is likely to be more sensible to\ndo so as you can make use of the indexes. However with PostgreSQL you may\nwant to add an index on <code class=\"docutils literal notranslate\">abs(change)</code> which would allow these queries to\nbe very efficient.</p>\n</aside>\n</section>\n<section id=\"a-bilateral-transformer-example\">\n<h2>A bilateral transformer example<a class=\"heading-anchor\" href=\"#a-bilateral-transformer-example\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The <code class=\"docutils literal notranslate\">AbsoluteValue</code> example we discussed previously is a transformation which\napplies to the left-hand side of the lookup. There may be some cases where you\nwant the transformation to be applied to both the left-hand side and the\nright-hand side. For instance, if you want to filter a queryset based on the\nequality of the left and right-hand side insensitively to some SQL function.</p>\n<p>Let's examine the simple example of case-insensitive transformation here. This\ntransformation isn't very useful in practice as Django already comes with a bunch\nof built-in case-insensitive lookups, but it will be a nice demonstration of\nbilateral transformations in a database-agnostic way.</p>\n<p>We define an <code class=\"docutils literal notranslate\">UpperCase</code> transformer which uses the SQL function <code class=\"docutils literal notranslate\">UPPER()</code> to\ntransform the values before comparison. We define\n<a class=\"reference internal\" href=\"/zh-hans/2.1/ref/models/lookups/#django.db.models.Transform.bilateral\" title=\"django.db.models.Transform.bilateral\"><code class=\"xref py py-attr docutils literal notranslate\">bilateral = True</code></a> to indicate that\nthis transformation should apply to both <code class=\"docutils literal notranslate\">lhs</code> and <code class=\"docutils literal notranslate\">rhs</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=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Transform\n\n<span class=\"k\">class</span> <span class=\"nc\">UpperCase</span><span class=\"p\">(</span>Transform<span class=\"p\">):</span>\n    lookup_name <span class=\"o\">=</span> <span class=\"s1\">&#39;upper&#39;</span>\n    function <span class=\"o\">=</span> <span class=\"s1\">&#39;UPPER&#39;</span>\n    bilateral <span class=\"o\">=</span> <span class=\"kc\">True</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=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> CharField<span class=\"p\">,</span> TextField\nCharField<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>UpperCase<span class=\"p\">)</span>\nTextField<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>UpperCase<span class=\"p\">)</span>\n</code></pre></div>\n<p>现在, 这个 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>Author.objects.filter(name__upper=&quot;doe&quot;)``查询集会生成一个像这样的不区分大小写的查询:</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>SELECT <span class=\"o\">...</span> WHERE UPPER<span class=\"p\">(</span><span class=\"s2\">&quot;author&quot;</span><span class=\"o\">.</span><span class=\"s2\">&quot;name&quot;</span><span class=\"p\">)</span> <span class=\"o\">=</span> UPPER<span class=\"p\">(</span><span class=\"s1\">&#39;doe&#39;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"writing-alternative-implementations-for-existing-lookups\">\n<h2>为现有的查找编写代替实现<a class=\"heading-anchor\" href=\"#writing-alternative-implementations-for-existing-lookups\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Sometimes different database vendors require different SQL for the same\noperation. For this example we will rewrite a custom implementation for\nMySQL for the NotEqual operator. Instead of <code class=\"docutils literal notranslate\">&lt;&gt;</code> we will be using <code class=\"docutils literal notranslate\">!=</code>\noperator. (Note that in reality almost all databases support both, including\nall the official databases supported by Django).</p>\n<p>我们可以通过使用 <code class=\"docutils literal notranslate\">as_mysql</code> 方法创建 <code class=\"docutils literal notranslate\">NotEqual</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=\"nc\">MySQLNotEqual</span><span class=\"p\">(</span>NotEqual<span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">as_mysql</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> compiler<span class=\"p\">,</span> connection<span class=\"p\">):</span>\n        lhs<span class=\"p\">,</span> lhs_params <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span>process_lhs<span class=\"p\">(</span>compiler<span class=\"p\">,</span> connection<span class=\"p\">)</span>\n        rhs<span class=\"p\">,</span> rhs_params <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span>process_rhs<span class=\"p\">(</span>compiler<span class=\"p\">,</span> connection<span class=\"p\">)</span>\n        params <span class=\"o\">=</span> lhs_params <span class=\"o\">+</span> rhs_params\n        <span class=\"k\">return</span> <span class=\"s1\">&#39;</span><span class=\"si\">%s</span><span class=\"s1\"> != </span><span class=\"si\">%s</span><span class=\"s1\">&#39;</span> <span class=\"o\">%</span> <span class=\"p\">(</span>lhs<span class=\"p\">,</span> rhs<span class=\"p\">),</span> params\n\nField<span class=\"o\">.</span>register_lookup<span class=\"p\">(</span>MySQLNotEqual<span class=\"p\">)</span>\n</code></pre></div>\n<p>We can then register it with <code class=\"docutils literal notranslate\">Field</code>. It takes the place of the original\n<code class=\"docutils literal notranslate\">NotEqual</code> class as it has the same <code class=\"docutils literal notranslate\">lookup_name</code>.</p>\n<p>When compiling a query, Django first looks for <code class=\"docutils literal notranslate\">as_%s % connection.vendor</code>\nmethods, and then falls back to <code class=\"docutils literal notranslate\">as_sql</code>. The vendor names for the in-built\nbackends are <code class=\"docutils literal notranslate\">sqlite</code>, <code class=\"docutils literal notranslate\">postgresql</code>, <code class=\"docutils literal notranslate\">oracle</code> and <code class=\"docutils literal notranslate\">mysql</code>.</p>\n</section>\n<section id=\"how-django-determines-the-lookups-and-transforms-which-are-used\">\n<h2>How Django determines the lookups and transforms which are used<a class=\"heading-anchor\" href=\"#how-django-determines-the-lookups-and-transforms-which-are-used\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>In some cases you may wish to dynamically change which <code class=\"docutils literal notranslate\">Transform</code> or\n<code class=\"docutils literal notranslate\">Lookup</code> is returned based on the name passed in, rather than fixing it. As\nan example, you could have a field which stores coordinates or an arbitrary\ndimension, and wish to allow a syntax like <code class=\"docutils literal notranslate\">.filter(coords__x7=4)</code> to return\nthe objects where the 7th coordinate has value 4. In order to do this, you\nwould override <code class=\"docutils literal notranslate\">get_lookup</code> with something like:</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=\"nc\">CoordinatesField</span><span class=\"p\">(</span>Field<span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">get_lookup</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> lookup_name<span class=\"p\">):</span>\n        <span class=\"k\">if</span> lookup_name<span class=\"o\">.</span>startswith<span class=\"p\">(</span><span class=\"s1\">&#39;x&#39;</span><span class=\"p\">):</span>\n            <span class=\"k\">try</span><span class=\"p\">:</span>\n                dimension <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span>lookup_name<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">:])</span>\n            <span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n                <span class=\"k\">pass</span>\n            <span class=\"k\">else</span><span class=\"p\">:</span>\n                <span class=\"k\">return</span> get_coordinate_lookup<span class=\"p\">(</span>dimension<span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span>get_lookup<span class=\"p\">(</span>lookup_name<span class=\"p\">)</span>\n</code></pre></div>\n<p>You would then define <code class=\"docutils literal notranslate\">get_coordinate_lookup</code> appropriately to return a\n<code class=\"docutils literal notranslate\">Lookup</code> subclass which handles the relevant value of <code class=\"docutils literal notranslate\">dimension</code>.</p>\n<p>There is a similarly named method called <code class=\"docutils literal notranslate\">get_transform()</code>. <code class=\"docutils literal notranslate\">get_lookup()</code>\nshould always return a <code class=\"docutils literal notranslate\">Lookup</code> subclass, and <code class=\"docutils literal notranslate\">get_transform()</code> a\n<code class=\"docutils literal notranslate\">Transform</code> subclass. It is important to remember that <code class=\"docutils literal notranslate\">Transform</code>\nobjects can be further filtered on, and <code class=\"docutils literal notranslate\">Lookup</code> objects cannot.</p>\n<p>When filtering, if there is only one lookup name remaining to be resolved, we\nwill look for a <code class=\"docutils literal notranslate\">Lookup</code>. If there are multiple names, it will look for a\n<code class=\"docutils literal notranslate\">Transform</code>. In the situation where there is only one name and a <code class=\"docutils literal notranslate\">Lookup</code>\nis not found, we look for a <code class=\"docutils literal notranslate\">Transform</code> and then the <code class=\"docutils literal notranslate\">exact</code> lookup on that\n<code class=\"docutils literal notranslate\">Transform</code>. All call sequences always end with a <code class=\"docutils literal notranslate\">Lookup</code>. To clarify:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">.filter(myfield__mylookup)</code> 将会调用 <code class=\"docutils literal notranslate\">myfield.get_lookup('mylookup')</code>.</p></li>\n<li><p><code class=\"docutils literal notranslate\">.filter(myfield__mytransform__mylookup)</code> 将会调用 <code class=\"docutils literal notranslate\">myfield.get_transform('mytransform')</code>, 接着调用 <code class=\"docutils literal notranslate\">mytransform.get_lookup('mylookup')</code>.</p></li>\n<li><p><code class=\"docutils literal notranslate\">.filter(myfield__mytransform)</code> will first call\n<code class=\"docutils literal notranslate\">myfield.get_lookup('mytransform')</code>, which will fail, so it will fall back\nto calling <code class=\"docutils literal notranslate\">myfield.get_transform('mytransform')</code> and then\n<code class=\"docutils literal notranslate\">mytransform.get_lookup('exact')</code>.</p></li>\n</ul>\n</section>","rootId":"custom-lookups","toc":[{"title":"一个简单的查找示例","anchor":"a-simple-lookup-example","children":[]},{"title":"让我们举一个简单的转换器示例","anchor":"a-simple-transformer-example","children":[]},{"title":"编写一个高效的 abs__lt 查找","anchor":"writing-an-efficient-abs-lt-lookup","children":[]},{"title":"A bilateral transformer example","anchor":"a-bilateral-transformer-example","children":[]},{"title":"为现有的查找编写代替实现","anchor":"writing-alternative-implementations-for-existing-lookups","children":[]},{"title":"How Django determines the lookups and transforms which are used","anchor":"how-django-determines-the-lookups-and-transforms-which-are-used","children":[]}],"breadcrumbs":[{"docname":"howto/index","title":"\"How-to\" guides","url":"/zh-hans/2.1/howto/"}],"prev":{"docname":"howto/custom-model-fields","title":"编写自定义 model fields","url":"/zh-hans/2.1/howto/custom-model-fields/"},"next":{"docname":"howto/custom-template-tags","title":"自定义模板（template）的标签（tags）和过滤器（filters）","url":"/zh-hans/2.1/howto/custom-template-tags/"},"formats":{"html":"/zh-hans/2.1/howto/custom-lookups/","markdown":"/zh-hans/2.1/howto/custom-lookups.md","json":"/zh-hans/2.1/howto/custom-lookups.json"},"source":"https://github.com/django/django/blob/stable/2.1.x/docs/howto/custom-lookups.txt","official":"https://docs.djangoproject.com/zh-hans/2.1/howto/custom-lookups/","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","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}