custom-lookups.txt 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. ==============
  2. Custom lookups
  3. ==============
  4. .. versionadded:: 1.7
  5. .. module:: django.db.models.lookups
  6. :synopsis: Custom lookups
  7. .. currentmodule:: django.db.models
  8. By default Django offers a wide variety of :ref:`built-in lookups
  9. <field-lookups>` for filtering (for example, ``exact`` and ``icontains``). This
  10. documentation explains how to write custom lookups and how to alter the working
  11. of existing lookups.
  12. A simple Lookup example
  13. ~~~~~~~~~~~~~~~~~~~~~~~
  14. Let's start with a simple custom lookup. We will write a custom lookup ``ne``
  15. which works opposite to ``exact``. ``Author.objects.filter(name__ne='Jack')``
  16. will translate to the SQL::
  17. "author"."name" <> 'Jack'
  18. This SQL is backend independent, so we don't need to worry about different
  19. databases.
  20. There are two steps to making this work. Firstly we need to implement the
  21. lookup, then we need to tell Django about it. The implementation is quite
  22. straightforward::
  23. from django.db.models import Lookup
  24. class NotEqual(Lookup):
  25. lookup_name = 'ne'
  26. def as_sql(self, qn, connection):
  27. lhs, lhs_params = self.process_lhs(qn, connection)
  28. rhs, rhs_params = self.process_rhs(qn, connection)
  29. params = lhs_params + rhs_params
  30. return '%s <> %s' % (lhs, rhs), params
  31. To register the ``NotEqual`` lookup we will just need to call
  32. ``register_lookup`` on the field class we want the lookup to be available. In
  33. this case, the lookup makes sense on all ``Field`` subclasses, so we register
  34. it with ``Field`` directly::
  35. from django.db.models.fields import Field
  36. Field.register_lookup(NotEqual)
  37. We can now use ``foo__ne`` for any field ``foo``. You will need to ensure that
  38. this registration happens before you try to create any querysets using it. You
  39. could place the implementation in a ``models.py`` file, or register the lookup
  40. in the ``ready()`` method of an ``AppConfig``.
  41. Taking a closer look at the implementation, the first required attribute is
  42. ``lookup_name``. This allows the ORM to understand how to interpret ``name__ne``
  43. and use ``NotEqual`` to generate the SQL. By convention, these names are always
  44. lowercase strings containing only letters, but the only hard requirement is
  45. that it must not contain the string ``__``.
  46. A ``Lookup`` works against two values, ``lhs`` and ``rhs``, standing for
  47. left-hand side and right-hand side. The left-hand side is usually a field
  48. reference, but it can be anything implementing the :ref:`query expression API
  49. <query-expression>`. The right-hand is the value given by the user. In the
  50. example ``Author.objects.filter(name__ne='Jack')``, the left-hand side is a
  51. reference to the ``name`` field of the ``Author`` model, and ``'Jack'`` is the
  52. right-hand side.
  53. We call ``process_lhs`` and ``process_rhs`` to convert them into the values we
  54. need for SQL. In the above example, ``process_lhs`` returns
  55. ``('"author"."name"', [])`` and ``process_rhs`` returns ``('"%s"', ['Jack'])``.
  56. In this example there were no parameters for the left hand side, but this would
  57. depend on the object we have, so we still need to include them in the
  58. parameters we return.
  59. Finally we combine the parts into a SQL expression with ``<>``, and supply all
  60. the parameters for the query. We then return a tuple containing the generated
  61. SQL string and the parameters.
  62. A simple transformer example
  63. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  64. The custom lookup above is great, but in some cases you may want to be able to
  65. chain lookups together. For example, let's suppose we are building an
  66. application where we want to make use of the ``abs()`` operator.
  67. We have an ``Experiment`` model which records a start value, end value and the
  68. change (start - end). We would like to find all experiments where the change
  69. was equal to a certain amount (``Experiment.objects.filter(change__abs=27)``),
  70. or where it did not exceed a certain amount
  71. (``Experiment.objects.filter(change__abs__lt=27)``).
  72. .. note::
  73. This example is somewhat contrived, but it demonstrates nicely the range of
  74. functionality which is possible in a database backend independent manner,
  75. and without duplicating functionality already in Django.
  76. We will start by writing a ``AbsoluteValue`` transformer. This will use the SQL
  77. function ``ABS()`` to transform the value before comparison::
  78. from django.db.models import Transform
  79. class AbsoluteValue(Transform):
  80. lookup_name = 'abs'
  81. def as_sql(self, qn, connection):
  82. lhs, params = qn.compile(self.lhs)
  83. return "ABS(%s)" % lhs, params
  84. Next, lets register it for ``IntegerField``::
  85. from django.db.models import IntegerField
  86. IntegerField.register_lookup(AbsoluteValue)
  87. We can now run the queries we had before.
  88. ``Experiment.objects.filter(change__abs=27)`` will generate the following SQL::
  89. SELECT ... WHERE ABS("experiments"."change") = 27
  90. By using ``Transform`` instead of ``Lookup`` it means we are able to chain
  91. further lookups afterwards. So
  92. ``Experiment.objects.filter(change__abs__lt=27)`` will generate the following
  93. SQL::
  94. SELECT ... WHERE ABS("experiments"."change") < 27
  95. Subclasses of ``Transform`` usually only operate on the left-hand side of the
  96. expression. Further lookups will work on the transformed value. Note that in
  97. this case where there is no other lookup specified, Django interprets
  98. ``change__abs=27`` as ``change__abs__exact=27``.
  99. When looking for which lookups are allowable after the ``Transform`` has been
  100. applied, Django uses the ``output_type`` attribute. We didn't need to specify
  101. this here as it didn't change, but supposing we were applying ``AbsoluteValue``
  102. to some field which represents a more complex type (for example a point
  103. relative to an origin, or a complex number) then we may have wanted to specify
  104. ``output_type = FloatField``, which will ensure that further lookups like
  105. ``abs__lte`` behave as they would for a ``FloatField``.
  106. Writing an efficient abs__lt lookup
  107. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  108. When using the above written ``abs`` lookup, the SQL produced will not use
  109. indexes efficiently in some cases. In particular, when we use
  110. ``change__abs__lt=27``, this is equivalent to ``change__gt=-27`` AND
  111. ``change__lt=27``. (For the ``lte`` case we could use the SQL ``BETWEEN``).
  112. So we would like ``Experiment.objects.filter(change__abs__lt=27)`` to generate
  113. the following SQL::
  114. SELECT .. WHERE "experiments"."change" < 27 AND "experiments"."change" > -27
  115. The implementation is::
  116. from django.db.models import Lookup
  117. class AbsoluteValueLessThan(Lookup):
  118. lookup_name = 'lt'
  119. def as_sql(self, qn, connection):
  120. lhs, lhs_params = qn.compile(self.lhs.lhs)
  121. rhs, rhs_params = self.process_rhs(qn, connection)
  122. params = lhs_params + rhs_params + lhs_params + rhs_params
  123. return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params
  124. AbsoluteValue.register_lookup(AbsoluteValueLessThan)
  125. There are a couple of notable things going on. First, ``AbsoluteValueLessThan``
  126. isn't calling ``process_lhs()``. Instead it skips the transformation of the
  127. ``lhs`` done by ``AbsoluteValue`` and uses the original ``lhs``. That is, we
  128. want to get ``27`` not ``ABS(27)``. Referring directly to ``self.lhs.lhs`` is
  129. safe as ``AbsoluteValueLessThan`` can be accessed only from the
  130. ``AbsoluteValue`` lookup, that is the ``lhs`` is always an instance of
  131. ``AbsoluteValue``.
  132. Notice also that as both sides are used multiple times in the query the params
  133. need to contain ``lhs_params`` and ``rhs_params`` multiple times.
  134. The final query does the inversion (``27`` to ``-27``) directly in the
  135. database. The reason for doing this is that if the self.rhs is something else
  136. than a plain integer value (for example an ``F()`` reference) we can't do the
  137. transformations in Python.
  138. .. note::
  139. In fact, most lookups with ``__abs`` could be implemented as range queries
  140. like this, and on most database backends it is likely to be more sensible to
  141. do so as you can make use of the indexes. However with PostgreSQL you may
  142. want to add an index on ``abs(change)`` which would allow these queries to
  143. be very efficient.
  144. Writing alternative implementations for existing lookups
  145. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  146. Sometimes different database vendors require different SQL for the same
  147. operation. For this example we will rewrite a custom implementation for
  148. MySQL for the NotEqual operator. Instead of ``<>`` we will be using ``!=``
  149. operator. (Note that in reality almost all databases support both, including
  150. all the official databases supported by Django).
  151. We can change the behavior on a specific backend by creating a subclass of
  152. ``NotEqual`` with a ``as_mysql`` method::
  153. class MySQLNotEqual(NotEqual):
  154. def as_mysql(self, qn, connection):
  155. lhs, lhs_params = self.process_lhs(qn, connection)
  156. rhs, rhs_params = self.process_rhs(qn, connection)
  157. params = lhs_params + rhs_params
  158. return '%s != %s' % (lhs, rhs), params
  159. Field.register_lookup(MySQLNotExact)
  160. We can then register it with ``Field``. It takes the place of the original
  161. ``NotEqual`` class as it has the same ``lookup_name``.
  162. When compiling a query, Django first looks for ``as_%s % connection.vendor``
  163. methods, and then falls back to ``as_sql``. The vendor names for the in-built
  164. backends are ``sqlite``, ``postgresql``, ``oracle`` and ``mysql``.
  165. .. _query-expression:
  166. The Query Expression API
  167. ~~~~~~~~~~~~~~~~~~~~~~~~
  168. A lookup can assume that the lhs responds to the query expression API.
  169. Currently direct field references, aggregates and ``Transform`` instances respond
  170. to this API.
  171. .. method:: as_sql(qn, connection)
  172. Responsible for producing the query string and parameters for the
  173. expression. The ``qn`` has a ``compile()`` method that can be used to
  174. compile other expressions. The ``connection`` is the connection used to
  175. execute the query.
  176. Calling expression.as_sql() directly is usually incorrect - instead
  177. ``qn.compile(expression)`` should be used. The ``qn.compile()`` method will
  178. take care of calling vendor-specific methods of the expression.
  179. .. method:: get_lookup(lookup_name)
  180. The ``get_lookup()`` method is used to fetch lookups. By default the
  181. lookup is fetched from the expression's output type in the same way
  182. described in registering and fetching lookup documentation below.
  183. It is possible to override this method to alter that behavior.
  184. .. method:: as_vendorname(qn, connection)
  185. Works like ``as_sql()`` method. When an expression is compiled by
  186. ``qn.compile()``, Django will first try to call ``as_vendorname()``, where
  187. vendorname is the vendor name of the backend used for executing the query.
  188. The vendorname is one of ``postgresql``, ``oracle``, ``sqlite`` or
  189. ``mysql`` for Django's built-in backends.
  190. .. attribute:: output_type
  191. The ``output_type`` attribute is used by the ``get_lookup()`` method to check for
  192. lookups. The output_type should be a field.
  193. Note that this documentation lists only the public methods of the API.
  194. Lookup reference
  195. ~~~~~~~~~~~~~~~~
  196. .. class:: Lookup
  197. In addition to the attributes and methods below, lookups also support
  198. ``as_sql`` and ``as_vendorname`` from the query expression API.
  199. .. attribute:: lhs
  200. The ``lhs`` (left-hand side) of a lookup tells us what we are comparing the
  201. rhs to. It is an object which implements the query expression API. This is
  202. likely to be a field, an aggregate or a subclass of ``Transform``.
  203. .. attribute:: rhs
  204. The ``rhs`` (right-hand side) of a lookup is the value we are comparing the
  205. left hand side to. It may be a plain value, or something which compiles
  206. into SQL, for example an ``F()`` object or a ``Queryset``.
  207. .. attribute:: lookup_name
  208. This class level attribute is used when registering lookups. It determines
  209. the name used in queries to trigger this lookup. For example, ``contains``
  210. or ``exact``. This should not contain the string ``__``.
  211. .. method:: process_lhs(qn, connection)
  212. This returns a tuple of ``(lhs_string, lhs_params)``. In some cases you may
  213. wish to compile ``lhs`` directly in your ``as_sql`` methods using
  214. ``qn.compile(self.lhs)``.
  215. .. method:: process_rhs(qn, connection)
  216. Behaves the same as ``process_lhs`` but acts on the right-hand side.
  217. Transform reference
  218. ~~~~~~~~~~~~~~~~~~~
  219. .. class:: Transform
  220. In addition to implementing the query expression API Transforms have the
  221. following methods and attributes.
  222. .. attribute:: lhs
  223. The ``lhs`` (left-hand-side) of a transform contains the value to be
  224. transformed. The ``lhs`` implements the query expression API.
  225. .. attribute:: lookup_name
  226. This class level attribute is used when registering lookups. It determines
  227. the name used in queries to trigger this lookup. For example, ``year``
  228. or ``dayofweek``. This should not contain the string ``__``.
  229. .. _lookup-registration-api:
  230. Registering and fetching lookups
  231. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  232. The lookup registration API is explained below.
  233. .. classmethod:: register_lookup(lookup)
  234. Registers the Lookup or Transform for the class. For example
  235. ``DateField.register_lookup(YearExact)`` will register ``YearExact`` for
  236. all ``DateFields`` in the project, but also for fields that are instances
  237. of a subclass of ``DateField`` (for example ``DateTimeField``).
  238. .. method:: get_lookup(lookup_name)
  239. Django uses ``get_lookup(lookup_name)`` to fetch lookups or transforms.
  240. The implementation of ``get_lookup()`` fetches lookups or transforms
  241. registered for the current class based on their lookup_name attribute.
  242. The lookup registration API is available for ``Transform`` and ``Field`` classes.