Opened 8 years ago
Closed 8 years ago
#27650 closed Bug (duplicate)
ForeignKey.validate asks db_for_read for the parent's database, not the target field's database
Reported by: | Sven Coenye | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.10 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Use the following in the database router:
def db_for_read(self, model, **hints): return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label, None)
with
DATABASE_APPS_MAPPING = { 'main_app': 'orders', 'support_app': 'legacy_db'}
and in main_app.models.py
from support_app.models import AdProgram class Order(models.Model): first_name = models.CharField(max_length = 15) last_name = models.CharField(max_length = 20) program = models.ForeignKey(AdProgram) # AdProgram resides in legacy_db
ForeignKey.validate() contains this code
using = router.db_for_read(model_instance.__class__, instance=model_instance)
When submitting a ModelForm for the Order model (containing all three fields), model_instance is the Order model for all fields, so in db_for_read(), model._meta.app_label is "main_app" for all fields. During the validation of the "program" field, this leads to the validation query being submitted to the wrong database and the application terminates in an uncaught ProgrammingError exception.
Changing the line above to
using = router.db_for_read(self.remote_field.model, instance=model_instance)
passes the model of the target field instead. model._meta.app_label in db_for_read() is now "support_app" and the "legacy_db" database is returned. For target models in the same application as model_instance, the field app_label will be the same as the model_instance app_label so the correct database is retrieved in non-cross database cases as well.
I realize my particular database router code may have an influence on this, but I couldn't see a way for a router to derive that it should retrieve the "legacy_db" database with the information it is provided.
All Django tests appear to pass with the change in place on the stable/1.10.x branch.
Change History (2)
comment:1 by , 8 years ago
Summary: | ForeignKey.validate asks for the wrong db_for_read → ForeignKey.validate asks db_for_read for the parent's database, not the target field's database |
---|
comment:2 by , 8 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
Duplicate of #26784 (fixed in master which will be Django 1.11).