#30628: Order by with union can result in invalid ORDER BY clause
-------------------------------------+-------------------------------------
               Reporter:  Julien     |          Owner:  nobody
  Enselme                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  2.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 When doing a union of 2 querysets and then doing an `order_by` on the
 resulting queryset, if we order on a field whose type is present multiple
 time, the ordering will be incorrect if the field we sort on is not the
 1st field of the query. Explicitly settings values with `.values('field1',
 'field2')` on the base querysets or after the union has no effect.

 For instance, with this model, with 2 `DecimalField`s and one
 `BooleanField`:

 {{{
 class Listing(models.Model):
     sale_price = models.DecimalField('Sale price', max_digits=10,
 decimal_places=2)
     yearly_rent = models.DecimalField('Yearly rent', max_digits=10,
 decimal_places=2)
     toto = models.BooleanField()
 }}}

 {{{
 # Create 2 qs.
 qs1 = Listing.objects.all()
 qs2 = Listing.objects.all()

 # Create the union QS.
 qs3 = qs1.union(qs2)

 # Order on the 1st decimal field. This prints (which is correct) :
 # SELECT "union_listing"."id", "union_listing"."sale_price",
 "union_listing"."yearly_rent" FROM "union_listing" UNION SELECT
 "union_listing"."id", "union_listing"."sale_price",
 "union_listing"."yearly_rent" FROM "union_listing" ORDER BY (2) ASC
 print(qs3.order_by('sale_price').query)
 # Order on the 2nd deciamal field. This will print the same query as above
 which is incorrect.
 print(qs3.order_by('yearly_rent').query)
 # Not ordering on a DecimalField. This is correct again.
 print(qs3.order_by('toto').query)
 }}}

 From the debugging I did, it seems to come from
 
[https://github.com/django/django/commit/bc7e288ca9554ac1a0a19941302dea19df1acd21
 this commit]: If I revert `def __eq__` back to what it was in Django 2.1
 
(https://github.com/django/django/blob/stable/2.1.x/django/db/models/expressions.py#L363)
 it works as normal again. The difference between the two methods that can
 explain this is that in Django 2.1, we have a check on the actual field
 instances thanks to `other_args[1]`, but in 2.2, because of
 `identity[2][1]` which is the class of the field, we can't distinguish two
 fields of the same type (please refer to the respective implementations to
 know the values of `other_args` and `identity`).

 [https://github.com/Jenselme/dj-test-unions Sample projet to reproduce]
 (sqlite db included) ([https://github.com/Jenselme/dj-test-
 unions/blob/master/union/models.py Model], [https://github.com/Jenselme
 /dj-test-unions/blob/master/test-script.py test file]). Steps:
 1. Install Django 2.2
 2. Run `DJANGO_SETTINGS_MODULE=testunion.settings python test-script.py`
 3. You will see the queries for `qs3.order_by('sale_price')` and
 `qs3.order_by('yearly_rent')` They are exactly the same whereas they
 should be different (one with `ORDER BY (1)` and the other with `ORDER BY
 (2)`).

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30628>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/051.046ced5ea3fd84654eaf13f98d5dfb25%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to