Opened 5 years ago

Closed 14 months ago

Last modified 14 months ago

#17520 closed Bug (fixed)

write queries on related managers use the db_for_read database

Reported by: Tobias McNulty Owned by: Sławek Ehlert
Component: Database layer (models, ORM) Version: 1.3
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

If you execute a write query (update() or delete()) on a queryset obtained from a related manager, it appears to use the db_for_read database instead of the db_for_write database for that query.

The attached patch reproduces the issue on trunk. It also exists in 1.3.1.

Attachments (3)

related_writes_use_write_db.diff (2.4 KB) - added by Tobias McNulty 5 years ago.
related_writes_use_write_db_v2.diff (2.4 KB) - added by Sławek Ehlert 4 years ago.
slightly modified version for better debugging
related_writes_use_write_db_m2m.diff (4.3 KB) - added by Sławek Ehlert 4 years ago.
test case for a ManyToMany scenario

Download all attachments as: .zip

Change History (13)

Changed 5 years ago by Tobias McNulty

comment:1 Changed 5 years ago by Tobias McNulty

The issue appears to be that query._db gets set when the queryset is created from a related manager. As such, when the QuerySet.db property checks to see what write db to use, it assumes using() has been called manually and does not call db_for_write on the router as it should.

comment:2 Changed 5 years ago by Tobias McNulty

Patch needs improvement: set

The patch only has a test currently (not sure why that got unset, I didn't uncheck anything).

comment:3 Changed 5 years ago by Aymeric Augustin

Triage Stage: UnreviewedAccepted

Indeed, the test case looks valid and fails.

comment:4 Changed 4 years ago by Sławek Ehlert

Owner: changed from nobody to Sławek Ehlert
Status: newassigned

comment:5 Changed 4 years ago by Sławek Ehlert

The problem seems to be very subtle. The thing is that the update method on queryset is using self.db which uses self._db to determine the database. When we have a related manager, the queryset from it comes with "polluted" _db (because of this ).

When i tried a simplest approach with cleaning the _db after getting the qs like this:

if self._db is None:
    qs._db = None

it turns out that there's some feature tests that gets broken. For example this (which I think isn't documented anywhere). Will try to propose something different.

Changed 4 years ago by Sławek Ehlert

slightly modified version for better debugging

Changed 4 years ago by Sławek Ehlert

test case for a ManyToMany scenario

comment:7 Changed 4 years ago by Sławek Ehlert

Patch needs improvement: unset

I attached a newer version of tobias patch.

I also added a patch with a test case that shows that the ManyToMany scenario is also broken (you can check it here also)

comment:8 Changed 4 years ago by Sławek Ehlert

Patch needs improvement: set

comment:9 Changed 14 months ago by Tim Graham

Resolution: fixed
Status: assignedclosed

Fixed in Django 1.7 with #13724 / 9595183d03cfd0d94ae2dd506a3d2b86cf5c74a7.

comment:10 Changed 14 months ago by Tobias McNulty

Many thanks!

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