11
22from collections import OrderedDict
33
4+ import django
45from django .db .models .constants import LOOKUP_SEP
6+ from django .db .models .expressions import Expression
57from django .db .models .lookups import Transform
68from django .utils import six
79
@@ -13,11 +15,47 @@ def lookups_for_field(model_field):
1315 lookups = []
1416
1517 for expr , lookup in six .iteritems (class_lookups (model_field )):
18+ if issubclass (lookup , Transform ) and django .VERSION >= (1 , 9 ):
19+ transform = lookup (Expression (model_field ))
20+ lookups += [
21+ LOOKUP_SEP .join ([expr , sub_expr ]) for sub_expr
22+ in lookups_for_transform (transform )
23+ ]
24+
25+ else :
26+ lookups .append (expr )
27+
28+ return lookups
29+
30+
31+ def lookups_for_transform (transform ):
32+ """
33+ Generates a list of subsequent lookup expressions for a transform.
34+
35+ Note:
36+ Infinite transform recursion is only prevented when the subsequent and
37+ passed in transforms are the same class. For example, the ``Unaccent``
38+ transform from ``django.contrib.postgres``.
39+ There is no cycle detection across multiple transforms. For example,
40+ ``a__b__a__b`` would continue to recurse. However, this is not currently
41+ a problem (no builtin transforms exhibit this behavior).
42+
43+ """
44+ lookups = []
45+
46+ for expr , lookup in six .iteritems (class_lookups (transform .output_field )):
1647 if issubclass (lookup , Transform ):
48+
49+ # type match indicates recursion.
50+ if type (transform ) == lookup :
51+ continue
52+
53+ sub_transform = lookup (transform )
1754 lookups += [
18- LOOKUP_SEP .join ([expr , transform ]) for transform
19- in lookups_for_field ( lookup ( model_field ). output_field )
55+ LOOKUP_SEP .join ([expr , sub_expr ]) for sub_expr
56+ in lookups_for_transform ( sub_transform )
2057 ]
58+
2159 else :
2260 lookups .append (expr )
2361
@@ -28,12 +66,12 @@ def class_lookups(model_field):
2866 """
2967 Get a compiled set of class_lookups for a model field.
3068 """
31- field_class = model_field . __class__
69+ field_class = type ( model_field )
3270 class_lookups = OrderedDict ()
3371
3472 # traverse MRO in reverse, as this puts standard
3573 # lookups before subclass transforms/lookups
36- for cls in field_class .mro ()[:: - 1 ] :
74+ for cls in reversed ( field_class .mro ()) :
3775 if hasattr (cls , 'class_lookups' ):
3876 class_lookups .update (getattr (cls , 'class_lookups' ))
3977
0 commit comments