Opened 9 years ago

Closed 9 years ago

#24837 closed New feature (fixed)

Unable to query DateField using DateRange

Reported by: Matthew Schinckel Owned by: Marc Tamlyn <marc.tamlyn@…>
Component: contrib.postgres Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

MyModel.objects.filter(datefield__contained_by=DateRange(...))

The lookup is not defined on that field:

FieldError: Unsupported lookup 'contained_by' for IntegerField or join on the field not permitted.

Change History (8)

comment:1 by Matthew Schinckel, 9 years ago

I have a partial implementation, showing the next possible issue.

That is, the date field is attempting to coerce the lookup value into a date:

======================================================================
ERROR: test_datetime_range (postgres_tests.test_ranges.TestQueringWithRanges)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/matt/Development/django/tests/postgres_tests/test_ranges.py", line 238, in test_datetime_range
    RangeLookupsModel.objects.filter(timestamp__contained_by=DateTimeTZRange('2015-01-01T09:00', '2015-05-04T23:55')),
  File "/Users/matt/Development/django/django/db/models/manager.py", line 125, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/matt/Development/django/django/db/models/query.py", line 781, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Users/matt/Development/django/django/db/models/query.py", line 799, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/Users/matt/Development/django/django/db/models/sql/query.py", line 1236, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/Users/matt/Development/django/django/db/models/sql/query.py", line 1262, in _add_q
    allow_joins=allow_joins, split_subq=split_subq,
  File "/Users/matt/Development/django/django/db/models/sql/query.py", line 1196, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/Users/matt/Development/django/django/db/models/sql/query.py", line 1095, in build_lookup
    return final_lookup(lhs, rhs)
  File "/Users/matt/Development/django/django/db/models/lookups.py", line 98, in __init__
    self.rhs = self.get_prep_lookup()
  File "/Users/matt/Development/django/django/db/models/lookups.py", line 136, in get_prep_lookup
    return self.lhs.output_field.get_prep_lookup(self.lookup_name, self.rhs)
  File "/Users/matt/Development/django/django/db/models/fields/__init__.py", line 734, in get_prep_lookup
    return self.get_prep_value(value)
  File "/Users/matt/Development/django/django/db/models/fields/__init__.py", line 1432, in get_prep_value
    value = super(DateTimeField, self).get_prep_value(value)
  File "/Users/matt/Development/django/django/db/models/fields/__init__.py", line 1288, in get_prep_value
    return self.to_python(value)
  File "/Users/matt/Development/django/django/db/models/fields/__init__.py", line 1391, in to_python
    parsed = parse_datetime(value)
  File "/Users/matt/Development/django/django/utils/dateparse.py", line 93, in parse_datetime
    match = datetime_re.match(value)
TypeError: expected string or buffer

https://github.com/schinckel/django/tree/query-with-ranges

comment:2 by Matthew Schinckel, 9 years ago

In the past, I have written a Range field, and a lookup to use Range types, however I was not using Lookup.process_rhs(qn, connection): instead I was first looking to see if the rhs argument was a range type, and if it was, then just letting that go through.

Part of that test was inspecting a string argument and seeing if it matched a regex, which is probably not as hygienic as actually processing the argument correctly.

comment:3 by Tim Graham, 9 years ago

Easy pickings: unset
Triage Stage: UnreviewedAccepted
Type: UncategorizedNew feature
Version: 1.8master

comment:4 by Marc Tamlyn, 9 years ago

I've made some progress, see https://github.com/django/django/compare/master...mjtamlyn:query-with-ranges

There is no operator for not_contained_by in postgres so I removed that pseudocode from schinckel's branch.

The code passes the tests but needs some serious tidying up to cover non-simple use cases (F objects, complex expressions with output types etc). Sadly a lot of explicit typecasting is required to make postgres not freak out. As a result, I think we likely should just support "matching" operations. For example this means big integer in a big integer range, datetime in a datetime range not a date range.

comment:5 by Matthew Schinckel, 9 years ago

Hmm. Is casting a datetime to a date (i.e., in python doing a .date()) going to give the same result as doing the comparison in the database with a datetime (contained by a daterange)?

My gut feeling is yes, but I may not have had enough coffee this morning to make an educated guess.

comment:6 by Matthew Schinckel, 9 years ago

I know it _should_ just work, but is it worth having tests for .exclude(...__contained_by=...) worthwhile?

comment:7 by Marc Tamlyn, 9 years ago

Triage Stage: AcceptedReady for checkin

comment:8 by Marc Tamlyn <marc.tamlyn@…>, 9 years ago

Owner: set to Marc Tamlyn <marc.tamlyn@…>
Resolution: fixed
Status: newclosed

In 7bda2d8:

Fixed #24837 -- fieldcontained_by=Range

Provide contained_by lookups for the equivalent single valued fields
related to the range field types. This acts as the opposite direction to
rangefieldcontains.

With thanks to schinckel for the idea and initial tests.

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