Opened 8 years ago

Closed 2 weeks ago

Last modified 2 weeks ago

#29762 closed Cleanup/optimization (fixed)

Document how database routers are used for related object access

Reported by: Vackar Afzal Owned by: VIZZARD-X
Component: Documentation Version: 2.0
Severity: Normal Keywords: oracle, multidb
Cc: Ülgen Sarıkavak 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 run a filter query like this:

my_instance = MyModel.objects.using('NonDefaultDBConn').filter(some_field='XXXX')
my_instance.local_field  #This works
my_instance.fk_field     #This doesn't work

It fails when trying to access my_instance.fk_field as it defaults to using the 'default' connection and not 'NonDefaultDBConn'

I would assume that when fetching a model using 'NonDefaultDBConn' that all subsequent lookups should go through 'NonDefaultDBConn' and not 'default'

If this is not the case, and this is expected behaviour how can I force fk lookups to go through 'NonDefaultDBConn'

I know I can run

MyLinkedModel.objects.filter(x_instance=my_instance)


But this is massively in-efficient both from a performance and code maintainability point of view.

I am using Django 2.0 with the Oracle backend.

Change History (19)

comment:1 by Tim Graham, 8 years ago

Resolution: needsinfo
Status: newclosed
Type: UncategorizedBug

The behavior you describe shouldn't happen. There are tests in Django's test suite that demonstrates. I also tried this:

dive = Book.objects.using('other').filter(title="Dive into Python")[0]
self.assertEqual(dive.editor.name, "Chris Mills")

Please provide a test case or a sample project that reproduces the problem you're seeing.

comment:2 by Vackar Afzal, 8 years ago

Resolution: needsinfo
Status: closednew

Apologies I should have been clearer with the problem, it's similar to what is being described here:
https://stackoverflow.com/questions/40457351/why-is-the-database-used-by-django-subqueries-not-sticky

My application has a single 'MetaSchema' and 'n' identical ApplicationSchemas.
The default connection points to the meta-schema, and the default router routes everything via 'defaut'

AppModel1 is not present in the default 'MetaSchema', but is present in all of the ApplicationSchemas

Running this fails on the second line:

obj =  AppModel1.objects.using('ApplicationSchema1').first()
obj.objects.fk_field # <--- This fails


However, looking at the router configuration for the tests, I think it may be a misconfiguration issue on my part. It looks like I need to setup the router as follows:

class TestRouter:
 
    def db_for_read(self, model, instance=None, **hints):
        if instance:
            return instance._state.db or 'default'
        return 'default'

       .....

Is this something worth mentioning / or highlighting in the Documentation?

comment:3 by Tim Graham, 8 years ago

The test router isn't used for the test that I linked to. I also tried this and it works fine in the test:

dive = Book.objects.using('other').first() 
self.assertEqual(dive.editor.name, "Chris Mills")

Are you using a database router in your project?

comment:4 by Vackar Afzal, 8 years ago

I am indeed, and that's probably what's causing the odd behaviour:

class MyRouter(object):

    def db_for_read(self, model, **hints):
        return 'default'

    def db_for_write(self, model, **hints):
        return 'default'

    def allow_relation(self, obj1, obj2, **hints):
       return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return db == 'default'

comment:5 by Tim Graham, 8 years ago

Component: Database layer (models, ORM)Documentation
Summary: Explicit 'Using' with foreign Key on modelDocument how database routers are used for related object access
Type: BugCleanup/optimization

Right, so you're overriding the default behavior of using the database from which the object was fetched.

comment:6 by Tim Graham, 8 years ago

Triage Stage: UnreviewedAccepted

comment:7 by Vackar Afzal, 8 years ago

Yes, that's correct - thanks for your help in resolving this issue.

comment:8 by Ülgen Sarıkavak, 2 years ago

Cc: Ülgen Sarıkavak added

comment:9 by VIZZARD-X, 5 months ago

Submitted a PR for this issue: https://github.com/django/django/pull/20292

This documents how related-object database selection works in multi-database
configurations, including the use of instance._state.db and the instance
hint passed to db_for_read() in routers.

comment:10 by VIZZARD-X, 5 months ago

Has patch: set

comment:11 by VIZZARD-X, 5 months ago

Recreated my fork and restored the branch. New PR:

https://github.com/django/django/pull/20310

Patch content remains valid and unchanged.

comment:12 by Vijaya Lakshmi Pokala, 8 weeks ago

Hi, I’d like to work on this ticket and submit a patch soon.

comment:13 by Nabeel , 6 weeks ago

I have opened a pull request for this documentation clarification.

PR: https://github.com/django/django/pull/20841/commits

comment:14 by VIZZARD-X, 4 weeks ago

Owner: changed from nobody to VIZZARD-X
Status: newassigned

comment:15 by blighj, 4 weeks ago

Patch needs improvement: set

comment:16 by VIZZARD-X, 3 weeks ago

Patch needs improvement: unset

comment:17 by blighj, 3 weeks ago

Triage Stage: AcceptedReady for checkin

I've reviewed the additional wording, it covers the topic of this ticket, it is relatively concise, and been through a couple of edits. I marking this ready for checkin.

comment:18 by Jacob Walls <jacobtylerwalls@…>, 2 weeks ago

Resolution: fixed
Status: assignedclosed

In edd8997:

Fixed #29762 -- Doc'd how database routers fetch related objects.

Thanks James Bligh for the review.

Co-authored-by: Jacob Walls <jacobtylerwalls@…>

comment:19 by Jacob Walls <jacobtylerwalls@…>, 2 weeks ago

In 00f0b0d2:

[6.0.x] Fixed #29762 -- Doc'd how database routers fetch related objects.

Thanks James Bligh for the review.

Co-authored-by: Jacob Walls <jacobtylerwalls@…>

Backport of edd899786851f6285abbc3c272f4f5ec0e48a74c from main.

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