mirror of https://github.com/django/django.git
Fixed #31039 -- Added support for contained_by lookup with AutoFields, SmallIntegerField, and DecimalField.
This commit is contained in:
parent
664521c56a
commit
5d674eac87
|
@ -199,9 +199,11 @@ DateTimeRangeField.register_lookup(DateTimeRangeContains)
|
|||
class RangeContainedBy(lookups.PostgresSimpleLookup):
|
||||
lookup_name = 'contained_by'
|
||||
type_mapping = {
|
||||
'smallint': 'int4range',
|
||||
'integer': 'int4range',
|
||||
'bigint': 'int8range',
|
||||
'double precision': 'numrange',
|
||||
'numeric': 'numrange',
|
||||
'date': 'daterange',
|
||||
'timestamp with time zone': 'tstzrange',
|
||||
}
|
||||
|
@ -209,13 +211,17 @@ class RangeContainedBy(lookups.PostgresSimpleLookup):
|
|||
|
||||
def process_rhs(self, compiler, connection):
|
||||
rhs, rhs_params = super().process_rhs(compiler, connection)
|
||||
cast_type = self.type_mapping[self.lhs.output_field.db_type(connection)]
|
||||
# Ignore precision for DecimalFields.
|
||||
db_type = self.lhs.output_field.cast_db_type(connection).split('(')[0]
|
||||
cast_type = self.type_mapping[db_type]
|
||||
return '%s::%s' % (rhs, cast_type), rhs_params
|
||||
|
||||
def process_lhs(self, compiler, connection):
|
||||
lhs, lhs_params = super().process_lhs(compiler, connection)
|
||||
if isinstance(self.lhs.output_field, models.FloatField):
|
||||
lhs = '%s::numeric' % lhs
|
||||
elif isinstance(self.lhs.output_field, models.SmallIntegerField):
|
||||
lhs = '%s::integer' % lhs
|
||||
return lhs, lhs_params
|
||||
|
||||
def get_prep_lookup(self):
|
||||
|
@ -226,6 +232,7 @@ models.DateField.register_lookup(RangeContainedBy)
|
|||
models.DateTimeField.register_lookup(RangeContainedBy)
|
||||
models.IntegerField.register_lookup(RangeContainedBy)
|
||||
models.FloatField.register_lookup(RangeContainedBy)
|
||||
models.DecimalField.register_lookup(RangeContainedBy)
|
||||
|
||||
|
||||
@RangeField.register_lookup
|
||||
|
|
|
@ -738,10 +738,14 @@ operators ``@>``, ``<@``, and ``&&`` respectively.
|
|||
<QuerySet [<Event: Soft play>]>
|
||||
|
||||
The ``contained_by`` lookup is also available on the non-range field types:
|
||||
:class:`~django.db.models.SmallAutoField`,
|
||||
:class:`~django.db.models.AutoField`, :class:`~django.db.models.BigAutoField`,
|
||||
:class:`~django.db.models.SmallIntegerField`,
|
||||
:class:`~django.db.models.IntegerField`,
|
||||
:class:`~django.db.models.BigIntegerField`,
|
||||
:class:`~django.db.models.FloatField`, :class:`~django.db.models.DateField`,
|
||||
and :class:`~django.db.models.DateTimeField`. For example::
|
||||
:class:`~django.db.models.DecimalField`, :class:`~django.db.models.FloatField`,
|
||||
:class:`~django.db.models.DateField`, and
|
||||
:class:`~django.db.models.DateTimeField`. For example::
|
||||
|
||||
>>> from psycopg2.extras import DateTimeTZRange
|
||||
>>> Event.objects.filter(start__contained_by=DateTimeTZRange(
|
||||
|
@ -750,6 +754,14 @@ and :class:`~django.db.models.DateTimeField`. For example::
|
|||
... )
|
||||
<QuerySet [<Event: Soft play>]>
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Support for :class:`~django.db.models.SmallAutoField`,
|
||||
:class:`~django.db.models.AutoField`,
|
||||
:class:`~django.db.models.BigAutoField`,
|
||||
:class:`~django.db.models.SmallIntegerField`, and
|
||||
:class:`~django.db.models.DecimalField` was added.
|
||||
|
||||
.. fieldlookup:: rangefield.overlap
|
||||
|
||||
``overlap``
|
||||
|
|
|
@ -90,6 +90,13 @@ Minor features
|
|||
:lookup:`rangefield.upper_inc`, and :lookup:`rangefield.upper_inf` allows
|
||||
querying :class:`~django.contrib.postgres.fields.RangeField` by a bound type.
|
||||
|
||||
* :lookup:`rangefield.contained_by` now supports
|
||||
:class:`~django.db.models.SmallAutoField`,
|
||||
:class:`~django.db.models.AutoField`,
|
||||
:class:`~django.db.models.BigAutoField`,
|
||||
:class:`~django.db.models.SmallIntegerField`, and
|
||||
:class:`~django.db.models.DecimalField`.
|
||||
|
||||
:mod:`django.contrib.redirects`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -124,6 +124,20 @@ class Migration(migrations.Migration):
|
|||
options=None,
|
||||
bases=None,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SmallAutoFieldModel',
|
||||
fields=[
|
||||
('id', models.SmallAutoField(verbose_name='ID', serialize=False, primary_key=True)),
|
||||
],
|
||||
options=None,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BigAutoFieldModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(verbose_name='ID', serialize=False, primary_key=True)),
|
||||
],
|
||||
options=None,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Scene',
|
||||
fields=[
|
||||
|
@ -237,6 +251,8 @@ class Migration(migrations.Migration):
|
|||
('float', models.FloatField(blank=True, null=True)),
|
||||
('timestamp', models.DateTimeField(blank=True, null=True)),
|
||||
('date', models.DateField(blank=True, null=True)),
|
||||
('small_integer', models.SmallIntegerField(blank=True, null=True)),
|
||||
('decimal_field', models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'required_db_vendor': 'postgresql',
|
||||
|
|
|
@ -93,6 +93,14 @@ class TextFieldModel(models.Model):
|
|||
return self.field
|
||||
|
||||
|
||||
class SmallAutoFieldModel(models.Model):
|
||||
id = models.SmallAutoField(primary_key=True)
|
||||
|
||||
|
||||
class BigAutoFieldModel(models.Model):
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
|
||||
|
||||
# Scene/Character/Line models are used to test full text search. They're
|
||||
# populated with content from Monty Python and the Holy Grail.
|
||||
class Scene(models.Model):
|
||||
|
@ -148,6 +156,8 @@ class RangeLookupsModel(PostgreSQLModel):
|
|||
float = models.FloatField(blank=True, null=True)
|
||||
timestamp = models.DateTimeField(blank=True, null=True)
|
||||
date = models.DateField(blank=True, null=True)
|
||||
small_integer = models.SmallIntegerField(blank=True, null=True)
|
||||
decimal_field = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
|
||||
|
||||
|
||||
class JSONModel(PostgreSQLModel):
|
||||
|
|
|
@ -11,7 +11,10 @@ from django.test.utils import isolate_apps
|
|||
from django.utils import timezone
|
||||
|
||||
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
|
||||
from .models import PostgreSQLModel, RangeLookupsModel, RangesModel
|
||||
from .models import (
|
||||
BigAutoFieldModel, PostgreSQLModel, RangeLookupsModel, RangesModel,
|
||||
SmallAutoFieldModel,
|
||||
)
|
||||
|
||||
try:
|
||||
from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange
|
||||
|
@ -354,6 +357,17 @@ class TestQueryingWithRanges(PostgreSQLTestCase):
|
|||
[objs[0]],
|
||||
)
|
||||
|
||||
def test_small_integer_field_contained_by(self):
|
||||
objs = [
|
||||
RangeLookupsModel.objects.create(small_integer=8),
|
||||
RangeLookupsModel.objects.create(small_integer=4),
|
||||
RangeLookupsModel.objects.create(small_integer=-1),
|
||||
]
|
||||
self.assertSequenceEqual(
|
||||
RangeLookupsModel.objects.filter(small_integer__contained_by=NumericRange(4, 6)),
|
||||
[objs[1]],
|
||||
)
|
||||
|
||||
def test_integer_range(self):
|
||||
objs = [
|
||||
RangeLookupsModel.objects.create(integer=5),
|
||||
|
@ -376,6 +390,19 @@ class TestQueryingWithRanges(PostgreSQLTestCase):
|
|||
[objs[0]]
|
||||
)
|
||||
|
||||
def test_decimal_field_contained_by(self):
|
||||
objs = [
|
||||
RangeLookupsModel.objects.create(decimal_field=Decimal('1.33')),
|
||||
RangeLookupsModel.objects.create(decimal_field=Decimal('2.88')),
|
||||
RangeLookupsModel.objects.create(decimal_field=Decimal('99.17')),
|
||||
]
|
||||
self.assertSequenceEqual(
|
||||
RangeLookupsModel.objects.filter(
|
||||
decimal_field__contained_by=NumericRange(Decimal('1.89'), Decimal('7.91')),
|
||||
),
|
||||
[objs[1]],
|
||||
)
|
||||
|
||||
def test_float_range(self):
|
||||
objs = [
|
||||
RangeLookupsModel.objects.create(float=5),
|
||||
|
@ -387,6 +414,39 @@ class TestQueryingWithRanges(PostgreSQLTestCase):
|
|||
[objs[0]]
|
||||
)
|
||||
|
||||
def test_small_auto_field_contained_by(self):
|
||||
objs = SmallAutoFieldModel.objects.bulk_create([
|
||||
SmallAutoFieldModel() for i in range(1, 5)
|
||||
])
|
||||
self.assertSequenceEqual(
|
||||
SmallAutoFieldModel.objects.filter(
|
||||
id__contained_by=NumericRange(objs[1].pk, objs[3].pk),
|
||||
),
|
||||
objs[1:3],
|
||||
)
|
||||
|
||||
def test_auto_field_contained_by(self):
|
||||
objs = RangeLookupsModel.objects.bulk_create([
|
||||
RangeLookupsModel() for i in range(1, 5)
|
||||
])
|
||||
self.assertSequenceEqual(
|
||||
RangeLookupsModel.objects.filter(
|
||||
id__contained_by=NumericRange(objs[1].pk, objs[3].pk),
|
||||
),
|
||||
objs[1:3],
|
||||
)
|
||||
|
||||
def test_big_auto_field_contained_by(self):
|
||||
objs = BigAutoFieldModel.objects.bulk_create([
|
||||
BigAutoFieldModel() for i in range(1, 5)
|
||||
])
|
||||
self.assertSequenceEqual(
|
||||
BigAutoFieldModel.objects.filter(
|
||||
id__contained_by=NumericRange(objs[1].pk, objs[3].pk),
|
||||
),
|
||||
objs[1:3],
|
||||
)
|
||||
|
||||
def test_f_ranges(self):
|
||||
parent = RangesModel.objects.create(decimals=NumericRange(0, 10))
|
||||
objs = [
|
||||
|
|
Loading…
Reference in New Issue