Opened 5 years ago

Closed 4 years ago

#31573 closed New feature (fixed)

Support order_by().update() on MySQL/MariaDB.

Reported by: Gerben Morsink Owned by: David Chorpash
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: update mysql order order_by
Cc: Adam Johnson Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When a UniqueConstraint is used on an IntegerField and we want to update multiple rows of that field at once, we need to make sure we select the right order before the update, since MySQL does not support a deferred update (see: https://dev.mysql.com/doc/refman/5.7/en/update.html).

I expected simply doing:

qs.order_by('-integerfield').update(integerfield=F('integerfield')+1) would work, but it seems the Django ORM strips the order_by from the query before execution, it gives a django.db.utils.IntegrityError: (1062, "Duplicate entry for key 'integerfield'").

Change History (14)

comment:1 by Simon Charette, 5 years ago

Keywords: mysql order order_by added; queryset removed
Summary: QuerySet.update().order_by() not working as expected on MySQLSupport update().order_by() on MySQL.
Triage Stage: UnreviewedAccepted
Type: BugNew feature
Version: 2.2master

MySQL seems to be only supported engine to allow UPDATE ... ORDER BY so it doesn't seem to be standard.

Since it's the documented way of dealing with the lack of deferrable unique constraint on MySQL I guess we could add support for it.

comment:2 by Simon Charette, 5 years ago

For anyone interested in implementing this feature you'll have to override the as_sql and pre_sql_setup methods of django.db.models.sql.compiler.SQLUpdateCompiler in django.db.backends.mysql.compiler.SQLUpdateCompiler to avoid clearing the ordering and append the ORDER BY ... clause to super().as_sql()[0].

Last edited 5 years ago by Simon Charette (previous) (diff)

comment:3 by Adam Johnson, 5 years ago

Cc: Adam Johnson added

comment:4 by Adam Johnson, 5 years ago

I imagine we'd also want a DB feature flag supports_update_order_by to gate this behaviour.

comment:5 by David Chorpash, 4 years ago

Owner: changed from nobody to David Chorpash
Status: newassigned

comment:6 by David Chorpash, 4 years ago

I would like to give a quick update.

I am able to get the following to work with MySQL on a unique constraint field:

QuerySet.order_by().update()

but I am still working on

QuerySet.update().order_by()

I'm also unsure how to add a feature flag here (I'm still new). I'm not sure if I just missed it in the documentation, but if anyone could point me in the right direction, that would be greatly appreciated!

comment:7 by Adam Johnson, 4 years ago

I don't think it's possible to support .update().order_by() because update() doesn't return another queryset, but instead actually does the update and returns the number of rows. Supporting only the .order_by().update() syntax seems fine to me.

in reply to:  7 comment:8 by David Chorpash, 4 years ago

Replying to Adam (Chainz) Johnson:

I don't think it's possible to support .update().order_by() because update() doesn't return another queryset, but instead actually does the update and returns the number of rows. Supporting only the .order_by().update() syntax seems fine to me.

Thanks for the info!

I added the following PR which allows for the use of .update().order_by().

comment:9 by Mariusz Felisiak, 4 years ago

Has patch: set
Patch needs improvement: set

comment:10 by David Chorpash, 4 years ago

Patch needs improvement: unset

Addressed comments in the PR. Added more test cases to handle a wider variety of use-cases.

comment:11 by Mariusz Felisiak, 4 years ago

Needs documentation: set
Patch needs improvement: set

comment:12 by Mariusz Felisiak, 4 years ago

Patch needs improvement: unset

comment:13 by Mariusz Felisiak, 4 years ago

Needs documentation: unset
Summary: Support update().order_by() on MySQL.Support order_by().update() on MySQL/MariaDB.
Triage Stage: AcceptedReady for checkin

comment:14 by Mariusz Felisiak <felisiak.mariusz@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In 779e615e:

Fixed #31573 -- Made QuerySet.update() respect ordering on MariaDB/MySQL.

Note: See TracTickets for help on using tickets.
Back to Top