Ticket #12328: t12328.diff

File t12328.diff, 5.0 KB (added by russellm, 5 years ago)

Fix for slicing/order_by problems in subqueries

  • django/db/models/query.py

    diff -r 55950a87e230 django/db/models/query.py
    a b  
    33"""
    44
    55from copy import deepcopy
     6from itertools import izip
    67
    78from django.db import connections, router, transaction, IntegrityError
    89from django.db.models.aggregates import Aggregate
     
    429430        # becoming too long.
    430431        seen_objs = None
    431432        while 1:
    432             # Collect all the objects to be deleted in this chunk, and all the
     433            # Collect a chunk of objects to be deleted, and then all the
    433434            # objects that are related to the objects that are to be deleted.
     435            # The chunking *isn't* done by slicing the del_query because we
     436            # need to maintain the query cache on del_query (see #12328)
    434437            seen_objs = CollectedObjects(seen_objs)
    435             for object in del_query[:CHUNK_SIZE]:
    436                 object._collect_sub_objects(seen_objs)
     438            for i,obj in izip(range(CHUNK_SIZE), del_query):
     439                obj._collect_sub_objects(seen_objs)
    437440
    438441            if not seen_objs:
    439442                break
  • django/db/models/sql/compiler.py

    diff -r 55950a87e230 django/db/models/sql/compiler.py
    a b  
    120120        """
    121121        Perform the same functionality as the as_sql() method, returning an
    122122        SQL string and parameters. However, the alias prefixes are bumped
    123         beforehand (in a copy -- the current query isn't changed) and any
    124         ordering is removed.
     123        beforehand (in a copy -- the current query isn't changed), and any
     124        ordering is removed if the query is unsliced.
    125125
    126126        Used when nesting this query inside another.
    127127        """
    128128        obj = self.query.clone()
    129         obj.clear_ordering(True)
     129        if obj.low_mark == 0 and obj.high_mark is None:
     130            # If there is no slicing in use, then we can safely drop all ordering
     131            obj.clear_ordering(True)
    130132        obj.bump_prefix()
    131133        return obj.get_compiler(connection=self.connection).as_sql()
    132134
  • tests/regressiontests/queries/models.py

    diff -r 55950a87e230 tests/regressiontests/queries/models.py
    a b  
    1919    from django.utils.itercompat import sorted
    2020
    2121class DumbCategory(models.Model):
    22     pass
     22    def __unicode__(self):
     23        return unicode(self.id)
    2324
    2425class NamedCategory(DumbCategory):
    2526    name = models.CharField(max_length=10)
  • tests/regressiontests/queries/tests.py

    diff -r 55950a87e230 tests/regressiontests/queries/tests.py
    a b  
    11import unittest
    2 from models import Tag, Annotation
     2
    33from django.db.models import Count
     4from django.test import TestCase
     5
     6from models import Tag, Annotation, DumbCategory
    47
    58class QuerysetOrderedTests(unittest.TestCase):
    69    """
    710    Tests for the Queryset.ordered attribute.
    811    """
    9    
     12
    1013    def test_no_default_or_explicit_ordering(self):
    1114        self.assertEqual(Annotation.objects.all().ordered, False)
    1215
    1316    def test_cleared_default_ordering(self):
    1417        self.assertEqual(Tag.objects.all().ordered, True)
    1518        self.assertEqual(Tag.objects.all().order_by().ordered, False)
    16        
     19
    1720    def test_explicit_ordering(self):
    1821        self.assertEqual(Annotation.objects.all().order_by('id').ordered, True)
    19        
     22
    2023    def test_order_by_extra(self):
    2124        self.assertEqual(Annotation.objects.all().extra(order_by=['id']).ordered, True)
    22        
     25
    2326    def test_annotated_ordering(self):
    2427        qs = Annotation.objects.annotate(num_notes=Count('notes'))
    2528        self.assertEqual(qs.ordered, False)
    2629        self.assertEqual(qs.order_by('num_notes').ordered, True)
    27        
    28  No newline at end of file
     30
     31
     32class SubqueryTests(TestCase):
     33    def setUp(self):
     34        DumbCategory.objects.create(id=1)
     35        DumbCategory.objects.create(id=2)
     36        DumbCategory.objects.create(id=3)
     37
     38    def test_ordered_subselect(self):
     39        "Subselects can't have a manually specified ordering"
     40        query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:2]).order_by('id')
     41        self.assertEquals(repr(query), "[<DumbCategory: 2>, <DumbCategory: 3>]")
     42
     43        query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[:2]).order_by('id')
     44        self.assertEquals(repr(query), "[<DumbCategory: 2>, <DumbCategory: 3>]")
     45
     46        query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[2:]).order_by('id')
     47        self.assertEquals(repr(query), "[<DumbCategory: 1>]")
     48
     49    def test_sliced_delete(self):
     50        DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:1]).delete()
     51        self.assertEquals(repr(DumbCategory.objects.order_by('id')), "[<DumbCategory: 1>, <DumbCategory: 2>]")
Back to Top