Commit 1f7b25c1 authored by Valentina Mukhamedzhanova's avatar Valentina Mukhamedzhanova Committed by Tim Graham
Browse files

Fixed #24986 -- Added support for annotations in DISTINCT queries.

parent 98bcdfa8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ answer newbie questions, and generally made Django that much better:
    Dmitri Fedortchenko <zeraien@gmail.com>
    Dmitry Jemerov <intelliyole@gmail.com>
    dne@mayonnaise.net
    Donald Harvey <donald@donaldharvey.co.uk>
    Donald Stufft <donald@stufft.io>
    Don Spaulding <donspauldingii@gmail.com>
    Doug Beck <doug@douglasbeck.com>
@@ -710,6 +711,7 @@ answer newbie questions, and generally made Django that much better:
    Tyler Tarabula <tyler.tarabula@gmail.com>
    Tyson Tate <tyson@fallingbullets.com>
    Unai Zalakain <unai@gisa-elkartea.org>
    Valentina Mukhamedzhanova <umirra@gmail.com>
    valtron
    Vasiliy Stavenko <stavenko@gmail.com>
    Vasil Vangelovski
+4 −1
Original line number Diff line number Diff line
@@ -548,6 +548,9 @@ class SQLCompiler(object):
            _, targets, alias, joins, path, _ = self._setup_joins(parts, opts, None)
            targets, alias, _ = self.query.trim_joins(targets, joins, path)
            for target in targets:
                if name in self.query.annotation_select:
                    result.append(name)
                else:
                    result.append("%s.%s" % (qn(alias), qn2(target.column)))
        return result

+11 −2
Original line number Diff line number Diff line
@@ -1292,9 +1292,15 @@ class Query(object):
            cur_names_with_path = (name, [])
            if name == 'pk':
                name = opts.pk.name

            field = None
            try:
                field = opts.get_field(name)
            except FieldDoesNotExist:
                if name in self.annotation_select:
                    field = self.annotation_select[name].output_field

            if field is not None:
                # Fields that contain one-to-many relations with a generic
                # model (like a GenericForeignKey) cannot generate reverse
                # relations and therefore cannot be used for reverse querying.
@@ -1305,8 +1311,11 @@ class Query(object):
                        "querying. If it is a GenericForeignKey, consider "
                        "adding a GenericRelation." % name
                    )
                try:
                    model = field.model._meta.concrete_model
            except FieldDoesNotExist:
                except AttributeError:
                    model = None
            else:
                # We didn't find the current field, so move position back
                # one step.
                pos -= 1
+2 −0
Original line number Diff line number Diff line
@@ -462,6 +462,8 @@ Models
  :attr:`~django.db.models.SlugField.allow_unicode` argument to allow Unicode
  characters in slugs.

* Added support for referencing annotations in ``QuerySet.distinct()``.

CSRF
^^^^

+36 −1
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@ from django.db.models import (
    F, BooleanField, CharField, Count, DateTimeField, ExpressionWrapper, Func,
    IntegerField, Sum, Value,
)
from django.test import TestCase
from django.db.models.functions import Lower
from django.test import TestCase, skipUnlessDBFeature
from django.utils import six

from .models import (
@@ -160,6 +161,40 @@ class NonAggregateAnnotationTestCase(TestCase):
        other_agg = Author.objects.aggregate(age_sum=Sum('age'))
        self.assertEqual(agg['otherage_sum'], other_agg['age_sum'])

    @skipUnlessDBFeature('can_distinct_on_fields')
    def test_distinct_on_with_annotation(self):
        store = Store.objects.create(
            name='test store',
            original_opening=datetime.datetime.now(),
            friday_night_closing=datetime.time(21, 00, 00),
        )
        names = [
            'Theodore Roosevelt',
            'Eleanor Roosevelt',
            'Franklin Roosevelt',
            'Ned Stark',
            'Catelyn Stark',
        ]
        for name in names:
            Employee.objects.create(
                store=store,
                first_name=name.split()[0],
                last_name=name.split()[1],
                age=30, salary=2000,
            )

        people = Employee.objects.annotate(
            name_lower=Lower('last_name'),
        ).distinct('name_lower')

        self.assertEqual(set(p.last_name for p in people), {'Stark', 'Roosevelt'})
        self.assertEqual(len(people), 2)

        people2 = Employee.objects.annotate(
            test_alias=F('store__name'),
        ).distinct('test_alias')
        self.assertEqual(len(people2), 1)

    def test_filter_annotation(self):
        books = Book.objects.annotate(
            is_book=Value(1, output_field=IntegerField())