Some rewording in docs
This commit is contained in:
parent
31b8faa627
commit
66649ff891
|
@ -7,56 +7,71 @@ Custom lookups
|
||||||
|
|
||||||
.. currentmodule:: django.db.models
|
.. currentmodule:: django.db.models
|
||||||
|
|
||||||
(This documentation is candidate for complete rewrite, but contains
|
|
||||||
useful information of how to test the current implementation.)
|
|
||||||
|
|
||||||
This documentation constains instructions of how to create custom lookups
|
|
||||||
for model fields.
|
|
||||||
|
|
||||||
Django's ORM works using lookup paths when building query filters and other
|
Django's ORM works using lookup paths when building query filters and other
|
||||||
query structures. For example in the query Book.filter(author__age__lte=30)
|
query conditions. For example in the query Book.filter(author__age__lte=30)
|
||||||
the author__age__lte is the lookup path.
|
the part "author__age__lte" is the lookup path.
|
||||||
|
|
||||||
The lookup path consist of three different part. First is the related lookups,
|
The lookup path consist of three different parts. First is the related
|
||||||
above part author refers to Book's related model Author. Second part of the
|
lookups. In the author__age__lte example the part author refers to Book's
|
||||||
lookup path is the final field, above this is Author's field age. Finally the
|
related model Author. Second part of the lookup path is the field. This is
|
||||||
lte part is commonly called just lookup (TODO: this nomenclature is confusing,
|
Author's age field in the example. Finally the lte part is commonly called
|
||||||
can we invent something better).
|
just lookup. Both the related lookups part and the final lookup part can
|
||||||
|
contain multiple parts, for example "author__friends__birthdate__year__lte"
|
||||||
|
has author, friends as related lookups, birthdate as the field and year, lte
|
||||||
|
as final lookup part.
|
||||||
|
|
||||||
This documentation concentrates on writing custom lookups, that is custom
|
This documentation concentrates on writing custom lookups. By writing custom
|
||||||
implementations for lte or any other lookup you wish to use.
|
lookups it is possible to control how Django interprets the final lookup part.
|
||||||
|
|
||||||
Django will fetch a ``Lookup`` class from the final field using the field's
|
Django will fetch a ``Lookup`` class from the final field using the field's
|
||||||
method get_lookup(lookup_name). This method can do three things:
|
method get_lookup(lookup_name). This method is allowed to do these things:
|
||||||
|
|
||||||
1. Return a Lookup class
|
1. Return a Lookup class
|
||||||
2. Raise a FieldError
|
2. Raise a FieldError
|
||||||
3. Return None
|
3. Return None
|
||||||
|
|
||||||
Above return None is only available during backwards compatibility period and
|
Returning None is only available during backwards compatibility period.
|
||||||
returning None will not be allowed in Django 1.9 or later. The interpretation
|
The interpretation is to use the old way of lookup hadling inside the ORM.
|
||||||
is to use the old way of lookup hadling inside the ORM.
|
|
||||||
|
|
||||||
The returned Lookup will be used to build the query.
|
|
||||||
|
|
||||||
The Lookup class
|
The Lookup class
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A Lookup operates on two values and produces boolean results. The values
|
||||||
|
are called lhs and rhs. The lhs is usually a field reference, but it can be
|
||||||
|
anything implementing the query expression API. The rhs is a value to compare
|
||||||
|
against.
|
||||||
|
|
||||||
The API is as follows:
|
The API is as follows:
|
||||||
|
|
||||||
.. attribute:: lookup_name
|
.. attribute:: lookup_name
|
||||||
|
|
||||||
A string used by Django to distinguish different lookups.
|
A string used by Django to distinguish different lookups. For example
|
||||||
|
'exact'.
|
||||||
|
|
||||||
.. method:: __init__(lhs, rhs)
|
.. method:: __init__(lhs, rhs)
|
||||||
|
|
||||||
The lhs and rhs are the field reference (reference to field age in the
|
The lhs is something implementing the query expression API. For example in
|
||||||
author__age__lte=30 example), and rhs is the value (30 in the example).
|
author__age__lte=30 the lhs is a Col instance referencing the age field of
|
||||||
|
author model. The rhs is the value to compare against. It can be Python value
|
||||||
|
(30 in the example) or SQL reference (produced by using F() or queryset for
|
||||||
|
example).
|
||||||
|
|
||||||
.. attribute:: Lookup.lhs
|
.. attribute:: Lookup.lhs
|
||||||
|
|
||||||
The left hand side part of this lookup. You can assume it implements the
|
The left hand side part of this lookup. You can assume it implements the
|
||||||
query part interface (TODO: write interface definition...).
|
query expression interface.
|
||||||
|
|
||||||
|
.. attribute:: Lookup.rhs
|
||||||
|
|
||||||
|
The value to compare against.
|
||||||
|
|
||||||
|
.. method:: Lookup.process_lhs(qn, connection)
|
||||||
|
|
||||||
|
Turns the lhs into query string + params.
|
||||||
|
|
||||||
|
.. method:: Lookup.process_rhs(qn, connection)
|
||||||
|
|
||||||
|
Turns the rhs into query string + params.
|
||||||
|
|
||||||
.. method:: Lookup.as_sql(qn, connection)
|
.. method:: Lookup.as_sql(qn, connection)
|
||||||
|
|
||||||
|
@ -79,42 +94,32 @@ qn.compile(part) instead of part.as_sql(qn, connection) so that 3rd party
|
||||||
backends have ability to customize the produced query string. More of this
|
backends have ability to customize the produced query string. More of this
|
||||||
later on.
|
later on.
|
||||||
|
|
||||||
The connection is the used connection.
|
The connection is the connection the SQL is compiled against.
|
||||||
|
|
||||||
.. method:: Lookup.process_lhs(qn, connection, lhs=None)
|
|
||||||
|
|
||||||
This method is used to convert the left hand side of the lookup into query
|
|
||||||
string. The left hand side can be a field reference or a nested lookup. The
|
|
||||||
lhs kwarg can be used to convert something else than self.lhs to query string.
|
|
||||||
|
|
||||||
.. method:: Lookup.process_rhs(qn, connection, rhs=None)
|
|
||||||
|
|
||||||
The process_rhs method is used to convert the right hand side into query string.
|
|
||||||
The rhs is the value given in the filter clause. It can be a raw value to
|
|
||||||
compare agains, a F() reference to another field or even a QuerySet.
|
|
||||||
|
|
||||||
In addition the Lookup class has some private methods - that is, implementing
|
In addition the Lookup class has some private methods - that is, implementing
|
||||||
just the above mentioned attributes and methods is not enough, instead you
|
just the above mentioned attributes and methods is not enough, instead you
|
||||||
should subclass Lookup.
|
must subclass Lookup.
|
||||||
|
|
||||||
The Extract class
|
The Extract class
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
An Extract is something that converts a value to another value in the query string.
|
An Extract is something that converts a value to another value in the query
|
||||||
For example you could have an Extract that procudes modulo 3 of the given value.
|
string. For example you could have an Extract that procudes modulo 3 of the
|
||||||
In SQL this would be something like "author"."age" % 3.
|
given value. In SQL this is something like "author"."age" % 3.
|
||||||
|
|
||||||
Extracts are used in nested lookups. The Extract class must implement the query
|
Extracts are used in nested lookups. The Extract class must implement the
|
||||||
part interface. In addition the Extract should must lookup_name attribute.
|
query part interface.
|
||||||
|
|
||||||
|
Extracts should be written by subclassing django.db.models.Extract.
|
||||||
|
|
||||||
A simple Lookup example
|
A simple Lookup example
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This is how to write a simple div3 lookup for IntegerField::
|
This is how to write a simple mod3 lookup for IntegerField::
|
||||||
|
|
||||||
from django.db.models import Lookup, IntegerField
|
from django.db.models import Lookup, IntegerField
|
||||||
class Div3(Lookup):
|
class Mod3(Lookup):
|
||||||
lookup_name = 'div3'
|
lookup_name = 'mod3'
|
||||||
|
|
||||||
def as_sql(self, qn, connection):
|
def as_sql(self, qn, connection):
|
||||||
lhs_sql, params = self.process_lhs(qn, connection)
|
lhs_sql, params = self.process_lhs(qn, connection)
|
||||||
|
@ -126,32 +131,32 @@ This is how to write a simple div3 lookup for IntegerField::
|
||||||
IntegerField.register_lookup(Div3)
|
IntegerField.register_lookup(Div3)
|
||||||
|
|
||||||
Now all IntegerFields or subclasses of IntegerField will have
|
Now all IntegerFields or subclasses of IntegerField will have
|
||||||
a div3 lookup. For example you could do Author.objects.filter(age__div3=2).
|
a mod3 lookup. For example you could do Author.objects.filter(age__mod3=2).
|
||||||
This query would return every author whose age % 3 == 2.
|
This query would return every author whose age % 3 == 2.
|
||||||
|
|
||||||
A simple nested lookup example
|
A simple nested lookup example
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Here is how to write an Extract and a Lookup for IntegerField. The example
|
Here is how to write an Extract and a Lookup for IntegerField. The example
|
||||||
lookup can be used similarly as the above div3 lookup, and in addition it
|
lookup can be used similarly as the above mod3 lookup, and in addition it
|
||||||
support nesting lookups::
|
support nesting lookups::
|
||||||
|
|
||||||
class Div3Extract(Extract):
|
class Mod3Extract(Extract):
|
||||||
lookup_name = 'div3'
|
lookup_name = 'mod3'
|
||||||
|
|
||||||
def as_sql(self, qn, connection):
|
def as_sql(self, qn, connection):
|
||||||
lhs, lhs_params = qn.compile(self.lhs)
|
lhs, lhs_params = qn.compile(self.lhs)
|
||||||
return '%s %%%% 3' % (lhs,), lhs_params
|
return '%s %%%% 3' % (lhs,), lhs_params
|
||||||
|
|
||||||
IntegerField.register_lookup(Div3Extract)
|
IntegerField.register_lookup(Mod3Extract)
|
||||||
|
|
||||||
Note that if you already added Div3 for IntegerField in the above
|
Note that if you already added Mod3 for IntegerField in the above
|
||||||
example, now Div3LookupWithExtract will override that lookup.
|
example, now Mod3Extract will override that lookup.
|
||||||
|
|
||||||
This lookup can be used like Div3 lookup, but in addition it supports
|
This lookup can be used like Mod3 lookup, but in addition it supports
|
||||||
nesting, too. The default output type for Extracts is the same type as the
|
nesting, too. The default output type for Extracts is the same type as the
|
||||||
lhs' output_type. So, the Div3Extract supports all the same lookups as
|
lhs' output_type. So, the Mod3Extract supports all the same lookups as
|
||||||
IntegerField. For example Author.objects.filter(age__div3__in=[1, 2])
|
IntegerField. For example Author.objects.filter(age__mod3__in=[1, 2])
|
||||||
returns all authors for which age % 3 in (1, 2).
|
returns all authors for which age % 3 in (1, 2).
|
||||||
|
|
||||||
A more complex nested lookup
|
A more complex nested lookup
|
||||||
|
|
Loading…
Reference in New Issue