1. ===========================
    
  2. How to write custom lookups
    
  3. ===========================
    
  4. 
    
  5. .. currentmodule:: django.db.models
    
  6. 
    
  7. Django offers a wide variety of :ref:`built-in lookups <field-lookups>` for
    
  8. filtering (for example, ``exact`` and ``icontains``). This documentation
    
  9. explains how to write custom lookups and how to alter the working of existing
    
  10. lookups. For the API references of lookups, see the :doc:`/ref/models/lookups`.
    
  11. 
    
  12. A lookup example
    
  13. ================
    
  14. 
    
  15. Let's start with a small custom lookup. We will write a custom lookup ``ne``
    
  16. which works opposite to ``exact``. ``Author.objects.filter(name__ne='Jack')``
    
  17. will translate to the SQL:
    
  18. 
    
  19. .. code-block:: sql
    
  20. 
    
  21.   "author"."name" <> 'Jack'
    
  22. 
    
  23. This SQL is backend independent, so we don't need to worry about different
    
  24. databases.
    
  25. 
    
  26. There are two steps to making this work. Firstly we need to implement the
    
  27. lookup, then we need to tell Django about it::
    
  28. 
    
  29.   from django.db.models import Lookup
    
  30. 
    
  31.   class NotEqual(Lookup):
    
  32.       lookup_name = 'ne'
    
  33. 
    
  34.       def as_sql(self, compiler, connection):
    
  35.           lhs, lhs_params = self.process_lhs(compiler, connection)
    
  36.           rhs, rhs_params = self.process_rhs(compiler, connection)
    
  37.           params = lhs_params + rhs_params
    
  38.           return '%s <> %s' % (lhs, rhs), params
    
  39. 
    
  40. To register the ``NotEqual`` lookup we will need to call ``register_lookup`` on
    
  41. the field class we want the lookup to be available for. In this case, the lookup
    
  42. makes sense on all ``Field`` subclasses, so we register it with ``Field``
    
  43. directly::
    
  44. 
    
  45.   from django.db.models import Field
    
  46.   Field.register_lookup(NotEqual)
    
  47. 
    
  48. Lookup registration can also be done using a decorator pattern::
    
  49. 
    
  50.     from django.db.models import Field
    
  51. 
    
  52.     @Field.register_lookup
    
  53.     class NotEqualLookup(Lookup):
    
  54.         # ...
    
  55. 
    
  56. We can now use ``foo__ne`` for any field ``foo``. You will need to ensure that
    
  57. this registration happens before you try to create any querysets using it. You
    
  58. could place the implementation in a ``models.py`` file, or register the lookup
    
  59. in the ``ready()`` method of an ``AppConfig``.
    
  60. 
    
  61. Taking a closer look at the implementation, the first required attribute is
    
  62. ``lookup_name``. This allows the ORM to understand how to interpret ``name__ne``
    
  63. and use ``NotEqual`` to generate the SQL. By convention, these names are always
    
  64. lowercase strings containing only letters, but the only hard requirement is
    
  65. that it must not contain the string ``__``.
    
  66. 
    
  67. We then need to define the ``as_sql`` method. This takes a ``SQLCompiler``
    
  68. object, called ``compiler``,  and the active database connection.
    
  69. ``SQLCompiler`` objects are not documented, but the only thing we need to know
    
  70. about them is that they have a ``compile()`` method which returns a tuple
    
  71. containing an SQL string, and the parameters to be interpolated into that
    
  72. string. In most cases, you don't need to use it directly and can pass it on to
    
  73. ``process_lhs()`` and ``process_rhs()``.
    
  74. 
    
  75. A ``Lookup`` works against two values, ``lhs`` and ``rhs``, standing for
    
  76. left-hand side and right-hand side. The left-hand side is usually a field
    
  77. reference, but it can be anything implementing the :ref:`query expression API
    
  78. <query-expression>`. The right-hand is the value given by the user. In the
    
  79. example ``Author.objects.filter(name__ne='Jack')``, the left-hand side is a
    
  80. reference to the ``name`` field of the ``Author`` model, and ``'Jack'`` is the
    
  81. right-hand side.
    
  82. 
    
  83. We call ``process_lhs`` and ``process_rhs`` to convert them into the values we
    
  84. need for SQL using the ``compiler`` object described before. These methods
    
  85. return tuples containing some SQL and the parameters to be interpolated into
    
  86. that SQL, just as we need to return from our ``as_sql`` method. In the above
    
  87. example, ``process_lhs`` returns ``('"author"."name"', [])`` and
    
  88. ``process_rhs`` returns ``('"%s"', ['Jack'])``. In this example there were no
    
  89. parameters for the left hand side, but this would depend on the object we have,
    
  90. so we still need to include them in the parameters we return.
    
  91. 
    
  92. Finally we combine the parts into an SQL expression with ``<>``, and supply all
    
  93. the parameters for the query. We then return a tuple containing the generated
    
  94. SQL string and the parameters.
    
  95. 
    
  96. A transformer example
    
  97. =====================
    
  98. 
    
  99. The custom lookup above is great, but in some cases you may want to be able to
    
  100. chain lookups together. For example, let's suppose we are building an
    
  101. application where we want to make use of the ``abs()`` operator.
    
  102. We have an ``Experiment`` model which records a start value, end value, and the
    
  103. change (start - end). We would like to find all experiments where the change
    
  104. was equal to a certain amount (``Experiment.objects.filter(change__abs=27)``),
    
  105. or where it did not exceed a certain amount
    
  106. (``Experiment.objects.filter(change__abs__lt=27)``).
    
  107. 
    
  108. .. note::
    
  109.     This example is somewhat contrived, but it nicely demonstrates the range of
    
  110.     functionality which is possible in a database backend independent manner,
    
  111.     and without duplicating functionality already in Django.
    
  112. 
    
  113. We will start by writing an ``AbsoluteValue`` transformer. This will use the SQL
    
  114. function ``ABS()`` to transform the value before comparison::
    
  115. 
    
  116.   from django.db.models import Transform
    
  117. 
    
  118.   class AbsoluteValue(Transform):
    
  119.       lookup_name = 'abs'
    
  120.       function = 'ABS'
    
  121. 
    
  122. Next, let's register it for ``IntegerField``::
    
  123. 
    
  124.   from django.db.models import IntegerField
    
  125.   IntegerField.register_lookup(AbsoluteValue)
    
  126. 
    
  127. We can now run the queries we had before.
    
  128. ``Experiment.objects.filter(change__abs=27)`` will generate the following SQL:
    
  129. 
    
  130. .. code-block:: sql
    
  131. 
    
  132.     SELECT ... WHERE ABS("experiments"."change") = 27
    
  133. 
    
  134. By using ``Transform`` instead of ``Lookup`` it means we are able to chain
    
  135. further lookups afterward. So
    
  136. ``Experiment.objects.filter(change__abs__lt=27)`` will generate the following
    
  137. SQL:
    
  138. 
    
  139. .. code-block:: sql
    
  140. 
    
  141.     SELECT ... WHERE ABS("experiments"."change") < 27
    
  142. 
    
  143. Note that in case there is no other lookup specified, Django interprets
    
  144. ``change__abs=27`` as ``change__abs__exact=27``.
    
  145. 
    
  146. This also allows the result to be used in ``ORDER BY`` and ``DISTINCT ON``
    
  147. clauses. For example ``Experiment.objects.order_by('change__abs')`` generates:
    
  148. 
    
  149. .. code-block:: sql
    
  150. 
    
  151.     SELECT ... ORDER BY ABS("experiments"."change") ASC
    
  152. 
    
  153. And on databases that support distinct on fields (such as PostgreSQL),
    
  154. ``Experiment.objects.distinct('change__abs')`` generates:
    
  155. 
    
  156. .. code-block:: sql
    
  157. 
    
  158.     SELECT ... DISTINCT ON ABS("experiments"."change")
    
  159. 
    
  160. When looking for which lookups are allowable after the ``Transform`` has been
    
  161. applied, Django uses the ``output_field`` attribute. We didn't need to specify
    
  162. this here as it didn't change, but supposing we were applying ``AbsoluteValue``
    
  163. to some field which represents a more complex type (for example a point
    
  164. relative to an origin, or a complex number) then we may have wanted to specify
    
  165. that the transform returns a ``FloatField`` type for further lookups. This can
    
  166. be done by adding an ``output_field`` attribute to the transform::
    
  167. 
    
  168.     from django.db.models import FloatField, Transform
    
  169. 
    
  170.     class AbsoluteValue(Transform):
    
  171.         lookup_name = 'abs'
    
  172.         function = 'ABS'
    
  173. 
    
  174.         @property
    
  175.         def output_field(self):
    
  176.             return FloatField()
    
  177. 
    
  178. This ensures that further lookups like ``abs__lte`` behave as they would for
    
  179. a ``FloatField``.
    
  180. 
    
  181. Writing an efficient ``abs__lt`` lookup
    
  182. =======================================
    
  183. 
    
  184. When using the above written ``abs`` lookup, the SQL produced will not use
    
  185. indexes efficiently in some cases. In particular, when we use
    
  186. ``change__abs__lt=27``, this is equivalent to ``change__gt=-27`` AND
    
  187. ``change__lt=27``. (For the ``lte`` case we could use the SQL ``BETWEEN``).
    
  188. 
    
  189. So we would like ``Experiment.objects.filter(change__abs__lt=27)`` to generate
    
  190. the following SQL:
    
  191. 
    
  192. .. code-block:: sql
    
  193. 
    
  194.     SELECT .. WHERE "experiments"."change" < 27 AND "experiments"."change" > -27
    
  195. 
    
  196. The implementation is::
    
  197. 
    
  198.   from django.db.models import Lookup
    
  199. 
    
  200.   class AbsoluteValueLessThan(Lookup):
    
  201.       lookup_name = 'lt'
    
  202. 
    
  203.       def as_sql(self, compiler, connection):
    
  204.           lhs, lhs_params = compiler.compile(self.lhs.lhs)
    
  205.           rhs, rhs_params = self.process_rhs(compiler, connection)
    
  206.           params = lhs_params + rhs_params + lhs_params + rhs_params
    
  207.           return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params
    
  208. 
    
  209.   AbsoluteValue.register_lookup(AbsoluteValueLessThan)
    
  210. 
    
  211. There are a couple of notable things going on. First, ``AbsoluteValueLessThan``
    
  212. isn't calling ``process_lhs()``. Instead it skips the transformation of the
    
  213. ``lhs`` done by ``AbsoluteValue`` and uses the original ``lhs``. That is, we
    
  214. want to get ``"experiments"."change"`` not ``ABS("experiments"."change")``.
    
  215. Referring directly to ``self.lhs.lhs`` is safe as ``AbsoluteValueLessThan``
    
  216. can be accessed only from the ``AbsoluteValue`` lookup, that is the ``lhs``
    
  217. is always an instance of ``AbsoluteValue``.
    
  218. 
    
  219. Notice also that  as both sides are used multiple times in the query the params
    
  220. need to contain ``lhs_params`` and ``rhs_params`` multiple times.
    
  221. 
    
  222. The final query does the inversion (``27`` to ``-27``) directly in the
    
  223. database. The reason for doing this is that if the ``self.rhs`` is something else
    
  224. than a plain integer value (for example an ``F()`` reference) we can't do the
    
  225. transformations in Python.
    
  226. 
    
  227. .. note::
    
  228.     In fact, most lookups with ``__abs`` could be implemented as range queries
    
  229.     like this, and on most database backends it is likely to be more sensible to
    
  230.     do so as you can make use of the indexes. However with PostgreSQL you may
    
  231.     want to add an index on ``abs(change)`` which would allow these queries to
    
  232.     be very efficient.
    
  233. 
    
  234. A bilateral transformer example
    
  235. ===============================
    
  236. 
    
  237. The ``AbsoluteValue`` example we discussed previously is a transformation which
    
  238. applies to the left-hand side of the lookup. There may be some cases where you
    
  239. want the transformation to be applied to both the left-hand side and the
    
  240. right-hand side. For instance, if you want to filter a queryset based on the
    
  241. equality of the left and right-hand side insensitively to some SQL function.
    
  242. 
    
  243. Let's examine case-insensitive transformations here. This transformation isn't
    
  244. very useful in practice as Django already comes with a bunch of built-in
    
  245. case-insensitive lookups, but it will be a nice demonstration of bilateral
    
  246. transformations in a database-agnostic way.
    
  247. 
    
  248. We define an ``UpperCase`` transformer which uses the SQL function ``UPPER()`` to
    
  249. transform the values before comparison. We define
    
  250. :attr:`bilateral = True <django.db.models.Transform.bilateral>` to indicate that
    
  251. this transformation should apply to both ``lhs`` and ``rhs``::
    
  252. 
    
  253.   from django.db.models import Transform
    
  254. 
    
  255.   class UpperCase(Transform):
    
  256.       lookup_name = 'upper'
    
  257.       function = 'UPPER'
    
  258.       bilateral = True
    
  259. 
    
  260. Next, let's register it::
    
  261. 
    
  262.   from django.db.models import CharField, TextField
    
  263.   CharField.register_lookup(UpperCase)
    
  264.   TextField.register_lookup(UpperCase)
    
  265. 
    
  266. Now, the queryset ``Author.objects.filter(name__upper="doe")`` will generate a case
    
  267. insensitive query like this:
    
  268. 
    
  269. .. code-block:: sql
    
  270. 
    
  271.     SELECT ... WHERE UPPER("author"."name") = UPPER('doe')
    
  272. 
    
  273. Writing alternative implementations for existing lookups
    
  274. ========================================================
    
  275. 
    
  276. Sometimes different database vendors require different SQL for the same
    
  277. operation. For this example we will rewrite a custom implementation for
    
  278. MySQL for the NotEqual operator. Instead of ``<>`` we will be using ``!=``
    
  279. operator. (Note that in reality almost all databases support both, including
    
  280. all the official databases supported by Django).
    
  281. 
    
  282. We can change the behavior on a specific backend by creating a subclass of
    
  283. ``NotEqual`` with an ``as_mysql`` method::
    
  284. 
    
  285.   class MySQLNotEqual(NotEqual):
    
  286.       def as_mysql(self, compiler, connection, **extra_context):
    
  287.           lhs, lhs_params = self.process_lhs(compiler, connection)
    
  288.           rhs, rhs_params = self.process_rhs(compiler, connection)
    
  289.           params = lhs_params + rhs_params
    
  290.           return '%s != %s' % (lhs, rhs), params
    
  291. 
    
  292.   Field.register_lookup(MySQLNotEqual)
    
  293. 
    
  294. We can then register it with ``Field``. It takes the place of the original
    
  295. ``NotEqual`` class as it has the same ``lookup_name``.
    
  296. 
    
  297. When compiling a query, Django first looks for ``as_%s % connection.vendor``
    
  298. methods, and then falls back to ``as_sql``. The vendor names for the in-built
    
  299. backends are ``sqlite``, ``postgresql``, ``oracle`` and ``mysql``.
    
  300. 
    
  301. How Django determines the lookups and transforms which are used
    
  302. ===============================================================
    
  303. 
    
  304. In some cases you may wish to dynamically change which ``Transform`` or
    
  305. ``Lookup`` is returned based on the name passed in, rather than fixing it. As
    
  306. an example, you could have a field which stores coordinates or an arbitrary
    
  307. dimension, and wish to allow a syntax like ``.filter(coords__x7=4)`` to return
    
  308. the objects where the 7th coordinate has value 4. In order to do this, you
    
  309. would override ``get_lookup`` with something like::
    
  310. 
    
  311.     class CoordinatesField(Field):
    
  312.         def get_lookup(self, lookup_name):
    
  313.             if lookup_name.startswith('x'):
    
  314.                 try:
    
  315.                     dimension = int(lookup_name[1:])
    
  316.                 except ValueError:
    
  317.                     pass
    
  318.                 else:
    
  319.                     return get_coordinate_lookup(dimension)
    
  320.             return super().get_lookup(lookup_name)
    
  321. 
    
  322. You would then define ``get_coordinate_lookup`` appropriately to return a
    
  323. ``Lookup`` subclass which handles the relevant value of ``dimension``.
    
  324. 
    
  325. There is a similarly named method called ``get_transform()``. ``get_lookup()``
    
  326. should always return a ``Lookup`` subclass, and ``get_transform()`` a
    
  327. ``Transform`` subclass. It is important to remember that ``Transform``
    
  328. objects can be further filtered on, and ``Lookup`` objects cannot.
    
  329. 
    
  330. When filtering, if there is only one lookup name remaining to be resolved, we
    
  331. will look for a ``Lookup``. If there are multiple names, it will look for a
    
  332. ``Transform``. In the situation where there is only one name and a ``Lookup``
    
  333. is not found, we look for a ``Transform`` and then the ``exact`` lookup on that
    
  334. ``Transform``. All call sequences always end with a ``Lookup``. To clarify:
    
  335. 
    
  336. - ``.filter(myfield__mylookup)`` will call ``myfield.get_lookup('mylookup')``.
    
  337. - ``.filter(myfield__mytransform__mylookup)`` will call
    
  338.   ``myfield.get_transform('mytransform')``, and then
    
  339.   ``mytransform.get_lookup('mylookup')``.
    
  340. - ``.filter(myfield__mytransform)`` will first call
    
  341.   ``myfield.get_lookup('mytransform')``, which will fail, so it will fall back
    
  342.   to calling ``myfield.get_transform('mytransform')`` and then
    
  343.   ``mytransform.get_lookup('exact')``.