#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.


Reply via email to