<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 自定義查找 # ``` New in Django 1.7. ``` Django為過濾提供了大量的內建的查找(例如,`exact`和`icontains`)。這篇文檔闡述了如何編寫自定義查找,以及如何修改現存查找的功能。關于查找的API參考,詳見查找API參考。 ## 一個簡單的查找示例 ## 讓我們從一個簡單的自定義查找開始。我們會編寫一個自定義查找`ne`,提供和`exact`相反的功能。`Author.objects.filter(name__ne='Jack')`會轉換成下面的SQL: ``` "author"."name" <> 'Jack' ``` 這條SQL是后端獨立的,所以我們并不需要擔心不同的數據庫。 實現它需要兩個步驟。首先我們需要實現這個查找,然后我們需要告訴Django它的信息。實現是十分簡單直接的: ``` from django.db.models import Lookup class NotEqual(Lookup): lookup_name = 'ne' def as_sql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params return '%s <> %s' % (lhs, rhs), params ``` 我們只需要在我們想讓查找應用的字段上調用`register_lookup`,來注冊`NotEqual`查找。這種情況下,查找在所有`Field`的子類都起作用,所以我們直接使用`Field`注冊它。 ``` from django.db.models.fields import Field Field.register_lookup(NotEqual) ``` 也可以使用裝飾器模式來注冊查找: ``` from django.db.models.fields import Field @Field.register_lookup class NotEqualLookup(Lookup): # ... ``` ``` Changed in Django 1.8: 新增了使用裝飾器模式的能力。 ``` 我們現在可以為任何`foo`字段使用 `foo__ne`。你需要確保在你嘗試創建使用它的任何查詢集之前完成注冊。你應該把實現放在`models.py`文件中,或者在`AppConfig`的`ready()`方法中注冊查找。 現在讓我們深入觀察這個實現,首先需要的屬性是`lookup_name`。這需要讓ORM理解如何去解釋`name__ne`,以及如何使用`NotEqual`來生成SQL。按照慣例,這些名字一般是只包含字母的小寫字符串,但是唯一硬性的要求是不能夠包含字符串`__`。 然后我們需要定義`as_sql`方法。這個方法需要傳入一個`SQLCompiler`對象,叫做 `compiler`,以及活動的數據庫連接。`SQLCompiler`對象并沒有記錄,但是我們需要知道的唯一一件事就是他們擁有`compile()`方法,這個方法返回一個元組,含有SQL字符串和要向字符串插入的參數。在多數情況下,你并不需要世界使用它,并且可以把它傳遞給`process_lhs()` 和 `process_rhs()`。 `Lookup`作用于兩個值,lhs和rhs,分別是左邊和右邊。左邊的值一般是個字段的引用,但是它可以是任何實現了查詢表達式API的對象。右邊的值由用戶提供。在例子`Author.objects.filter(name__ne='Jack')`中,左邊的值是`Author`模型的`name` 字段的引用,右邊的值是`'Jack'`。 我們可以調用 `process_lhs` 和`process_rhs` 來將它們轉換為我們需要的SQL值,使用之前我們描述的`compiler` 對象。 最后我們用`<>`將這些部分組合成SQL表達式,然后將所有參數用在查詢中。然后我們返回一個元組,包含生成的SQL字符串以及參數。 ## 一個簡單的轉換器示例 ## 上面的自定義轉換器是極好的,但是一些情況下你可能想要把查找放在一起。例如,假設我們構建一個應用,想要利用`abs()` 操作符。我們有用一個`Experiment`模型,它記錄了起始值,終止值,以及變化量(起始值 - 終止值)。我們想要尋找所有變化量等于一個特定值的實驗(`Experiment.objects.filter(change__abs=27)`),或者沒有達到指定值的實驗(`Experiment.objects.filter(change__abs__lt=27)`)。 > 注意 > > 這個例子一定程度上很不自然,但是很好地展示了數據庫后端獨立的功能范圍,并且沒有重復實現Django中已有的功能。 我們從編寫`AbsoluteValue`轉換器來開始。這會用到SQL函數`ABS()`,來在比較之前轉換值。 ``` from django.db.models import Transform class AbsoluteValue(Transform): lookup_name = 'abs' def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) return "ABS(%s)" % lhs, params ``` 接下來,為`IntegerField`注冊它: ``` from django.db.models import IntegerField IntegerField.register_lookup(AbsoluteValue) ``` 我們現在可以執行之前的查詢。`Experiment.objects.filter(change__abs=27)`會生成下面的SQL: ``` SELECT ... WHERE ABS("experiments"."change") = 27 ``` 通過使用`Transform`來替代`Lookup`,這說明了我們能夠把以后更多的查找放到一起。所以`Experiment.objects.filter(change__abs__lt=27)`會生成以下的SQL: ``` SELECT ... WHERE ABS("experiments"."change") < 27 ``` 注意在沒有指定其他查找的情況中,Django會將 `change__abs=27` 解釋為`change__abs__exact=27`。 當尋找在 `Transform`之后,哪個查找可以使用的時候,Django使用`output_field`屬性。因為它并沒有修改,我們在這里并不指定,但是假設我們在一些字段上應用AbsoluteValue,這些字段代表了一個更復雜的類型(比如說與原點(origin)相關的一個點,或者一個復數(complex number))。之后我們可能想指定,轉換要為進一步的查找返回`FloatField`類型。這可以通過向轉換添加`output_field` 屬性來實現: ``` from django.db.models import FloatField, Transform class AbsoluteValue(Transform): lookup_name = 'abs' def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) return "ABS(%s)" % lhs, params @property def output_field(self): return FloatField() ``` 這確保了更進一步的查找,像`abs__lte`的行為和對`FloatField`表現的一樣。 ## 編寫高效的 `abs__lt` 查找 ## 當我們使用上面編寫的`abs`查找的時候,在一些情況下,生成的SQL并不會高效使用索引。尤其是我們使用`change__abs__lt=27`的時候,這等價于`change__gt=-27 AND change__lt=27`。(對于`lte` 的情況,我們可以使用 SQL子句`BETWEEN`)。 所以我們想讓`Experiment.objects.filter(change__abs__lt=27)`生成以下SQL: ``` SELECT .. WHERE "experiments"."change" < 27 AND "experiments"."change" > -27 ``` 它的實現為: ``` from django.db.models import Lookup class AbsoluteValueLessThan(Lookup): lookup_name = 'lt' def as_sql(self, compiler, connection): lhs, lhs_params = compiler.compile(self.lhs.lhs) rhs, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params + lhs_params + rhs_params return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params AbsoluteValue.register_lookup(AbsoluteValueLessThan) ``` 有一些值得注意的事情。首先,`AbsoluteValueLessThan`并不調用`process_lhs()`。而是它跳過了由`AbsoluteValue`完成的`lhs`,并且使用原始的`lhs`。這就是說,我們想要得到`27` 而不是`ABS(27)`。直接引用`self.lhs.lhs`是安全的,因為 `AbsoluteValueLessThan`只能夠通過` AbsoluteValue `查找來訪問,這就是說 `lhs`始終是`AbsoluteValue`的實例。 也要注意,就像兩邊都要在查詢中使用多次一樣,參數也需要多次包含`lhs_params` 和`rhs_params`。 最終的實現直接在數據庫中執行了反轉 (27變為 -27) 。這樣做的原因是如果`self.rhs`不是一個普通的整數值(比如是一個`F()`引用),我們在Python中不能執行這一轉換。 > 注意 > > 實際上,大多數帶有__abs的查找都實現為這種范圍查詢,并且在大多數數據庫后端中它更可能執行成這樣,就像你可以利用索引一樣。然而在PostgreSQL中,你可能想要向abs(change) 中添加索引,這會使查詢更高效。 ## 一個雙向轉換器的示例 ## 我們之前討論的,`AbsoluteValue`的例子是一個只應用在查找左側的轉換。可能有一些情況,你想要把轉換同時應用在左側和右側。比如,你想過濾一個基于左右側相等比較操作的查詢集,在執行一些SQL函數之后它們是大小寫不敏感的。 讓我們測試一下這一大小寫不敏感的轉換的簡單示例。這個轉換在實踐中并不是十分有用,因為Django已經自帶了一些自建的大小寫不敏感的查找,但是它是一個很好的,數據庫無關的雙向轉換示例。 我們定義使用SQL 函數`UPPER()`的`UpperCase `轉換器,來在比較前轉換這些值。我們定義了`bilateral = True`來表明轉換同時作用在`lhs` 和`rhs`上面: ``` from django.db.models import Transform class UpperCase(Transform): lookup_name = 'upper' bilateral = True def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) return "UPPER(%s)" % lhs, params ``` 接下來,讓我們注冊它: ``` from django.db.models import CharField, TextField CharField.register_lookup(UpperCase) TextField.register_lookup(UpperCase) ``` 現在,查詢集`Author.objects.filter(name__upper="doe")`會生成像這樣的大小寫不敏感查詢: ``` SELECT ... WHERE UPPER("author"."name") = UPPER('doe') ``` ## 為現存查找編寫自動的實現 ## 有時不同的數據庫供應商對于相同的操作需要不同的SQL。對于這個例子,我們會為MySQL重新編寫一個自定義的,`NotEqual`操作的實現。我們會使用 `!=` 而不是 `<>`操作符。(注意實際上幾乎所有數據庫都支持這兩個,包括所有Django支持的官方數據庫)。 我們可以通過創建帶有`as_mysql`方法的`NotEqual`的子類來修改特定后端上的行為。 ``` class MySQLNotEqual(NotEqual): def as_mysql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params return '%s != %s' % (lhs, rhs), params Field.register_lookup(MySQLNotEqual) ``` 我們可以在`Field`中注冊它。它取代了原始的`NotEqual`類,由于它具有相同的`lookup_name`。 當編譯一個查詢的時候,Django首先尋找`as_%s % connection.vendor`方法,然后回退到 `as_sql`。內建后端的供應商名稱是 sqlite,postgresql, oracle 和mysql。 ## Django如何決定使用查找還是轉換 ## 有些情況下,你可能想要動態修改基于傳遞進來的名稱, `Transform` 或者 `Lookup`哪個會返回,而不是固定它。比如,你擁有可以儲存搭配( coordinate)或者任意一個維度(dimension)的字段,并且想讓類似于`.filter(coords__x7=4)`的語法返回第七個搭配值為4的對象。為了這樣做,你可以用一些東西覆寫`get_lookup`,比如: ``` class CoordinatesField(Field): def get_lookup(self, lookup_name): if lookup_name.startswith('x'): try: dimension = int(lookup_name[1:]) except ValueError: pass finally: return get_coordinate_lookup(dimension) return super(CoordinatesField, self).get_lookup(lookup_name) ``` 之后你應該合理定義`get_coordinate_lookup`。來返回一個 `Lookup`的子類,它處理`dimension`的相關值。 有一個名稱相似的方法叫做`get_transform()`。`get_lookup()`應該始終返回 `Lookup` 的子類,而`get_transform()` 返回`Transform` 的子類。記住`Transform` 對象可以進一步過濾,而 `Lookup` 對象不可以,這非常重要。 過濾的時候,如果還剩下只有一個查找名稱要處理,它會尋找`Lookup`。如果有多個名稱,它會尋找`Transform`。在只有一個名稱并且 Lookup找不到的情況下,會尋找`Transform`,之后尋找在`Transform`上面的`exact`查找。所有調用的語句都以一個`Lookup`結尾。解釋一下: + `.filter(myfield__mylookup)`會調用 `myfield.get_lookup('mylookup')`。 + `.filter(myfield__mytransform__mylookup)` 會調用 `myfield.get_transform('mytransform')`,然后調用`mytransform.get_lookup('mylookup')`。 + `.filter(myfield__mytransform)` 會首先調用 `myfield.get_lookup('mytransform')`,這樣會失敗,所以它會回退來調用 `myfield.get_transform('mytransform')` ,之后是 `mytransform.get_lookup('exact')`。 > 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Custom lookups](https://docs.djangoproject.com/en/1.8/howto/custom-lookups/)。 > > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。 > > [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看