Ticket #17728: 17728-2.diff

File 17728-2.diff, 10.4 KB (added by aaugustin, 3 years ago)
  • tests/modeltests/timezones/tests.py

     
    2525from django.utils.unittest import skipIf, skipUnless
    2626
    2727from .forms import EventForm, EventSplitForm, EventModelForm
    28 from .models import Event, MaybeEvent, Timestamp
     28from .models import Event, MaybeEvent, Session, SessionEvent, Timestamp
    2929
    3030
    3131# These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time)
     
    231231            'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20),
    232232        })
    233233
     234    def test_query_annotation(self):
     235        # Only min and max make sense for datetimes.
     236        morning = Session.objects.create(name='morning')
     237        afternoon = Session.objects.create(name='afternoon')
     238        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20), session=afternoon)
     239        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30), session=afternoon)
     240        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40), session=morning)
     241        morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40)
     242        afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
     243        self.assertQuerysetEqual(
     244                Session.objects.annotate(dt=Min('events__dt')).order_by('dt'),
     245                [morning_min_dt, afternoon_min_dt],
     246                transform=lambda d: d.dt)
     247        self.assertQuerysetEqual(
     248                Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt),
     249                [morning_min_dt],
     250                transform=lambda d: d.dt)
     251        self.assertQuerysetEqual(
     252                Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt),
     253                [afternoon_min_dt],
     254                transform=lambda d: d.dt)
     255
    234256    def test_query_dates(self):
    235257        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
    236258        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
     
    412434            'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT),
    413435        })
    414436
     437    def test_query_annotation(self):
     438        # Only min and max make sense for datetimes.
     439        morning = Session.objects.create(name='morning')
     440        afternoon = Session.objects.create(name='afternoon')
     441        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT), session=afternoon)
     442        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), session=afternoon)
     443        SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT), session=morning)
     444        morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT)
     445        afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
     446        self.assertQuerysetEqual(
     447                Session.objects.annotate(dt=Min('events__dt')).order_by('dt'),
     448                [morning_min_dt, afternoon_min_dt],
     449                transform=lambda d: d.dt)
     450        self.assertQuerysetEqual(
     451                Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt),
     452                [morning_min_dt],
     453                transform=lambda d: d.dt)
     454        self.assertQuerysetEqual(
     455                Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt),
     456                [afternoon_min_dt],
     457                transform=lambda d: d.dt)
     458
    415459    def test_query_dates(self):
    416460        # Same comment as in test_query_date_related_filters.
    417461        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  • tests/modeltests/timezones/models.py

     
    66class MaybeEvent(models.Model):
    77    dt = models.DateTimeField(blank=True, null=True)
    88
     9class Session(models.Model):
     10    name = models.CharField(max_length=20)
     11
     12class SessionEvent(models.Model):
     13    dt = models.DateTimeField()
     14    session = models.ForeignKey(Session, related_name='events')
     15
    916class Timestamp(models.Model):
    1017    created = models.DateTimeField(auto_now_add=True)
    1118    updated = models.DateTimeField(auto_now=True)
  • django/db/models/sql/aggregates.py

     
    22Classes to represent the default SQL aggregate functions
    33"""
    44
    5 class AggregateField(object):
    6     """An internal field mockup used to identify aggregates in the
    7     data-conversion parts of the database backend.
    8     """
    9     def __init__(self, internal_type):
    10         self.internal_type = internal_type
     5from django.db.models.fields import IntegerField, FloatField
    116
    12     def get_internal_type(self):
    13         return self.internal_type
     7# Fake fields used to identify aggregate types in data-conversion operations.
     8ordinal_aggregate_field = IntegerField()
     9computed_aggregate_field = FloatField()
    1410
    15 ordinal_aggregate_field = AggregateField('IntegerField')
    16 computed_aggregate_field = AggregateField('FloatField')
    17 
    1811class Aggregate(object):
    1912    """
    2013    Default SQL Aggregate.
  • django/db/models/sql/where.py

     
    1010from django.utils import tree
    1111from django.db.models.fields import Field
    1212from django.db.models.sql.datastructures import EmptyResultSet, FullResultSet
     13from django.db.models.sql.aggregates import Aggregate
    1314
    1415# Connection types
    1516AND = 'AND'
     
    3031    the correct SQL).
    3132
    3233    The children in this tree are usually either Q-like objects or lists of
    33     [table_alias, field_name, db_type, lookup_type, value_annotation,
    34     params]. However, a child could also be any class with as_sql() and
    35     relabel_aliases() methods.
     34    [table_alias, field_name, db_type, lookup_type, value_annotation, params].
     35    However, a child could also be any class with as_sql() and relabel_aliases() methods.
    3636    """
    3737    default = AND
    3838
     
    5454            # emptiness and transform any non-empty values correctly.
    5555            value = list(value)
    5656
    57         # The "annotation" parameter is used to pass auxilliary information
     57        # The "value_annotation" parameter is used to pass auxilliary information
    5858        # about the value(s) to the query construction. Specifically, datetime
    5959        # and empty values need special handling. Other types could be used
    6060        # here in the future (using Python types is suggested for consistency).
    6161        if isinstance(value, datetime.datetime):
    62             annotation = datetime.datetime
     62            value_annotation = datetime.datetime
    6363        elif hasattr(value, 'value_annotation'):
    64             annotation = value.value_annotation
     64            value_annotation = value.value_annotation
    6565        else:
    66             annotation = bool(value)
     66            value_annotation = bool(value)
    6767
    6868        if hasattr(obj, "prepare"):
    6969            value = obj.prepare(lookup_type, value)
    70             super(WhereNode, self).add((obj, lookup_type, annotation, value),
    71                 connector)
    72             return
    7370
    74         super(WhereNode, self).add((obj, lookup_type, annotation, value),
    75                 connector)
     71        super(WhereNode, self).add(
     72                (obj, lookup_type, value_annotation, value), connector)
    7673
    7774    def as_sql(self, qn, connection):
    7875        """
     
    133130    def make_atom(self, child, qn, connection):
    134131        """
    135132        Turn a tuple (table_alias, column_name, db_type, lookup_type,
    136         value_annot, params) into valid SQL.
     133        value_annotation, params) into valid SQL.
    137134
    138135        Returns the string for the SQL fragment and the parameters to use for
    139136        it.
    140137        """
    141         lvalue, lookup_type, value_annot, params_or_value = child
    142         if hasattr(lvalue, 'process'):
     138        lvalue, lookup_type, value_annotation, params_or_value = child
     139        if isinstance(lvalue, Constraint):
    143140            try:
    144141                lvalue, params = lvalue.process(lookup_type, params_or_value, connection)
    145142            except EmptyShortCircuit:
    146143                raise EmptyResultSet
     144        elif isinstance(lvalue, Aggregate):
     145            params = lvalue.field.get_db_prep_lookup(lookup_type, params_or_value, connection)
    147146        else:
    148             params = Field().get_db_prep_lookup(lookup_type, params_or_value,
    149                 connection=connection, prepared=True)
     147            raise TypeError("'make_atom' expects a Constraint or an Aggregate "
     148                            "as the first item of its 'child' argument.")
     149
    150150        if isinstance(lvalue, tuple):
    151151            # A direct database column lookup.
    152152            field_sql = self.sql_for_columns(lvalue, qn, connection)
     
    154154            # A smart object with an as_sql() method.
    155155            field_sql = lvalue.as_sql(qn, connection)
    156156
    157         if value_annot is datetime.datetime:
     157        if value_annotation is datetime.datetime:
    158158            cast_sql = connection.ops.datetime_cast_sql()
    159159        else:
    160160            cast_sql = '%s'
     
    168168        if (len(params) == 1 and params[0] == '' and lookup_type == 'exact'
    169169            and connection.features.interprets_empty_strings_as_nulls):
    170170            lookup_type = 'isnull'
    171             value_annot = True
     171            value_annotation = True
    172172
    173173        if lookup_type in connection.operators:
    174174            format = "%s %%s %%s" % (connection.ops.lookup_cast(lookup_type),)
     
    177177                              extra), params)
    178178
    179179        if lookup_type == 'in':
    180             if not value_annot:
     180            if not value_annotation:
    181181                raise EmptyResultSet
    182182            if extra:
    183183                return ('%s IN %s' % (field_sql, extra), params)
     
    206206                    params)
    207207        elif lookup_type == 'isnull':
    208208            return ('%s IS %sNULL' % (field_sql,
    209                 (not value_annot and 'NOT ' or '')), ())
     209                (not value_annotation and 'NOT ' or '')), ())
    210210        elif lookup_type == 'search':
    211211            return (connection.ops.fulltext_search_sql(field_sql), params)
    212212        elif lookup_type in ('regex', 'iregex'):
Back to Top