#27650: ForeignKey.validate asks for the wrong db_for_read
-------------------------------------+-------------------------------------
               Reporter:  Sven       |          Owner:  nobody
  Coenye                             |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  1.10
  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          |
-------------------------------------+-------------------------------------
 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.

--
Ticket URL: <https://code.djangoproject.com/ticket/27650>
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 django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.f9833348e77b981abe4ec1d54b2eec88%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to