diff options
| author | Sergey Fedoseev <fedoseev.sergey@gmail.com> | 2017-12-06 15:58:02 +0500 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2017-12-12 17:11:58 -1000 |
| commit | 10bfa876be59feec24bb6a40fa11bece808ee405 (patch) | |
| tree | 54842b45827be14a0921815588328f46b313a501 | |
| parent | 7c7bc6391a3e83566f9ace59955e63503bc76cee (diff) | |
Refs #27985 -- Reallowed using __exact=None as an alias for __isnull=True if a custom lookup class with lookup_name != None is registered as the exact lookup.
Regression in 58da81a5a372a69f0bac801c412b57f3cce5f188 and prerequisite
for refs #28896.
| -rw-r--r-- | django/db/models/sql/query.py | 19 | ||||
| -rw-r--r-- | tests/custom_lookups/tests.py | 17 |
2 files changed, 27 insertions, 9 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 81fcd6921f..7c88144fd2 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1068,23 +1068,24 @@ class Query: lhs = self.try_transform(lhs, name) # First try get_lookup() so that the lookup takes precedence if the lhs # supports both transform and lookup for the name. - lookup_class = lhs.get_lookup(lookups[-1]) + lookup_name = lookups[-1] + lookup_class = lhs.get_lookup(lookup_name) if not lookup_class: if lhs.field.is_relation: - raise FieldError('Related Field got invalid lookup: {}'.format(lookups[-1])) + raise FieldError('Related Field got invalid lookup: {}'.format(lookup_name)) # A lookup wasn't found. Try to interpret the name as a transform # and do an Exact lookup against it. - lhs = self.try_transform(lhs, lookups[-1]) - lookup_class = lhs.get_lookup('exact') - - if not lookup_class: - return + lhs = self.try_transform(lhs, lookup_name) + lookup_name = 'exact' + lookup_class = lhs.get_lookup(lookup_name) + if not lookup_class: + return lookup = lookup_class(lhs, rhs) # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all # uses of None as a query value. if lookup.rhs is None: - if lookup.lookup_name not in ('exact', 'iexact'): + if lookup_name not in ('exact', 'iexact'): raise ValueError("Cannot use None as a query value") return lhs.get_lookup('isnull')(lhs, True) @@ -1093,7 +1094,7 @@ class Query: # DEFAULT_DB_ALIAS isn't nice but it's the best that can be done here. # A similar thing is done in is_nullable(), too. if (connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and - lookup.lookup_name == 'exact' and lookup.rhs == ''): + lookup_name == 'exact' and lookup.rhs == ''): return lhs.get_lookup('isnull')(lhs, True) return lookup diff --git a/tests/custom_lookups/tests.py b/tests/custom_lookups/tests.py index bdb27a224a..9661aebc49 100644 --- a/tests/custom_lookups/tests.py +++ b/tests/custom_lookups/tests.py @@ -240,6 +240,23 @@ class LookupTests(TestCase): models.DateField._unregister_lookup(YearTransform) models.DateField._unregister_lookup(YearTransform, custom_transform_name) + def test_custom_exact_lookup_none_rhs(self): + """ + __exact=None is transformed to __isnull=True if a custom lookup class + with lookup_name != 'exact' is registered as the `exact` lookup. + """ + class CustomExactLookup(models.Lookup): + lookup_name = 'somecustomlookup' + + field = Author._meta.get_field('birthdate') + OldExactLookup = field.get_lookup('exact') + author = Author.objects.create(name='author', birthdate=None) + try: + type(field).register_lookup(Exactly, 'exact') + self.assertEqual(Author.objects.get(birthdate__exact=None), author) + finally: + type(field).register_lookup(OldExactLookup, 'exact') + def test_basic_lookup(self): a1 = Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) |
