Opened 8 days ago

Closed 23 hours ago

Last modified 23 hours ago

#28896 closed Bug (fixed)

GeoDjango PointField fails to generate query if filtering on a NULL value

Reported by: William Li Owned by: Sergey Fedoseev
Component: GIS Version: 2.0
Severity: Release blocker Keywords: PointField GeoDjango
Cc: Sergey Fedoseev Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Environment:

  • Python 3.6.3
  • postgres 9.6.6
  • Django 2.0

Note: This was working under Django 1.11.7.

# models.py
from django.contrib.gis.db import models

class Restaurant(models.Model):
  location = models.PointField(blank=True, null=True, db_index=True)
# tests.py
from django.test import TestCase
from restaurants.models import Restaurant


class RestaurantTestCase(TestCase):
    def test_no_location(self):
        # Failing query
        Restaurant.objects.filter(location=None)
$ python manage.py testCreating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_no_location (restaurants.tests.RestaurantTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/wli/projects/point-field-bug/restaurants/tests.py", line 7, in test_no_location
    Restaurant.objects.filter(location=None)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/query.py", line 836, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/query.py", line 854, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1252, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1276, in _add_q
    split_subq=split_subq,
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1214, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1084, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/contrib/gis/db/models/lookups.py", line 23, in __init__
    super().__init__(lhs, rhs)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/lookups.py", line 18, in __init__
    self.rhs = self.get_prep_lookup()
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/db/models/lookups.py", line 68, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "/home/wli/.envs/point-field-bug/lib/python3.6/site-packages/django/contrib/gis/db/models/fields.py", line 188, in get_prep_value
    raise ValueError('Cannot use object with type %s for a spatial lookup parameter.' % type(obj).__name__)
ValueError: Cannot use object with type NoneType for a spatial lookup parameter.

----------------------------------------------------------------------
Ran 1 test in 0.005s

FAILED (errors=1)
Destroying test database for alias 'default'...

Workaround:

Restaurant.objects.extra(where=['location IS NULL'])

Change History (9)

comment:1 Changed 8 days ago by William Li

This is a suspicious commit, but I'm not well-versed enough in the query internals to know for sure: https://github.com/django/django/commit/3b56f2191df0a437740182d49efe3be16c4d0d58

comment:2 Changed 8 days ago by Tim Graham

Cc: Sergey Fedoseev added
Component: Database layer (models, ORM)GIS
Keywords: QuerySet.extra removed
Severity: NormalRelease blocker
Triage Stage: UnreviewedAccepted

Bisected to 58da81a5a372a69f0bac801c412b57f3cce5f188. I would imagine that __isnull=True works, but we should either restore the old behavior of accepting None or document the backwards incompatibility.

comment:3 Changed 8 days ago by Sergey Fedoseev

Owner: changed from nobody to Sergey Fedoseev
Status: newassigned

comment:4 Changed 8 days ago by Sergey Fedoseev

Has patch: set

comment:5 Changed 5 days ago by Tim Graham

Triage Stage: AcceptedReady for checkin

comment:6 Changed 23 hours ago by Tim Graham <timograham@…>

In 10bfa876:

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.

comment:7 Changed 23 hours ago by Tim Graham <timograham@…>

Resolution: fixed
Status: assignedclosed

In da71e4bb:

Fixed #28896 -- Reallowed filtering a queryset with GeometryField=None.

Regression in 58da81a5a372a69f0bac801c412b57f3cce5f188.

comment:8 Changed 23 hours ago by Tim Graham <timograham@…>

In a5c6040:

[2.0.x] 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.

Backport of 10bfa876be59feec24bb6a40fa11bece808ee405 from master

comment:9 Changed 23 hours ago by Tim Graham <timograham@…>

In ce26ec0:

[2.0.x] Fixed #28896 -- Reallowed filtering a queryset with GeometryField=None.

Regression in 58da81a5a372a69f0bac801c412b57f3cce5f188.

Backport of da71e4bb086593b5ca76bf698358d27ead2cfed2 from master

Note: See TracTickets for help on using tickets.
Back to Top