#16187: Refactor lookup system
-------------------------------------+-------------------------------------
Reporter: UloPe | Owner: UloPe
Type: | Status: new
Cleanup/optimization | Version: master
Component: Database layer | Resolution:
(models, ORM) | Triage Stage: Accepted
Severity: Normal | Needs documentation: 0
Keywords: | Patch needs improvement: 1
Has patch: 1 | UI/UX: 0
Needs tests: 0 |
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Comment (by akaariai):
I think I know how to implement the nested lookups. Here is the idea: A
lookup is something that has get_lookup() method if it supports nesting.
Nesting is supported for those lookups that transform the column value in
some way (for example, `__year` lookup will extract the year from the
column). These lookups will respond to as_sql() method. Similarly fields
have get_lookup() method. Finally a new class Column is introduced, this
too responds to as_sql() method.
So for example `mydatefield__year__exact=2000` would resolve as follows:
1. resolve field (mydatefield from table "mytable" found).
2. field.get_lookup('year') -> resolves to YearLookup instance which is
given a Column("mytable", "mydatefield") as lhs value.
3. resolve 'exact' from the 'year' lookup. Will return an ExactLookup
which is given the YearLookup as lhs value.
4. as the ExactLookup is a final lookup, it will be given 2000 as a
value (set_lookup_value() maybe?).
5. The ExactLookup is added to WhereNode as is (no Constraint objects,
just plain where.add(found_lookup, connector)).
When transforming the value to SQL at compiler stage the following things
happen:
1. The ExactLookup.as_sql() is called.
2. The ExactLookup will call lhs.as_sql(), thus the YearLookup as_sql()
is called.
3. The YearLookup.as_sql() is called, this will again call the given
Column's as_sql() and resolve to "mytable"."mydatefield".
4. The YearLookup will wrap the "mytable"."mydatefield" to EXTRACT
'YEAR' FROM "mytable"."mydatefield".
5. The ExactLookup will return EXTRACT 'YEAR' FROM
"mytable"."mydatefield" = %s, (2000,).
So, a lookup .as_sql() works in two ways depending if the field has been
given a value or not - it either does a transformation of the value (cast,
extract, ...), or it produces boolean condition. There are a lot of
details to make this work correctly. How to treat SQLEvaluators,
subqueries, many special cases like isnull checks, how to handle
nullability - certain lookups can transform non-null values to non-
nullable values, how to handle return type information - from Column
through lookup transformations, what is the final type? And so on...
Still, the above idea should have a solid foundation for making things
work cleanly. For backwards compatibility I think the best approach is to
have the current code as much as-is somewhere, and use that for the
backwards compatibility period. The main problem is
field.get[_db]_prep_lookup() methods which needs to be called both for new
code and old code... Lets see how to make that work nicely.
--
Ticket URL: <https://code.djangoproject.com/ticket/16187#comment:20>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.