#26162: Data loss when ManyToManyField refers to the wrong table
-------------------------------------+-------------------------------------
     Reporter:  Ian-Foote            |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  1.8
  (models, ORM)                      |
     Severity:  Release blocker      |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by charettes):

 * cc: charettes (added)


Comment:

 I suspect this is caused by colliding `related_query_name`s.

 When you ''hide'' a related manager by setting `related_name` to a value
 ending with `'+'` the `related_query_name` is still set to its default
 value (based on the model name).

 If we explicit the model definitions created by Django here's what we
 have:

 {{{#!python
 class Bar(models.Model):
     class Meta:
         app_label = 'app1'

 class Foo(models.Model):
     bars = models.ManyToManyField(Bar, related_name='+',
 through='app1.FooBar')

     class Meta:
         app_label = 'app1'

 class FooBar(models.Model):
     bar = models.ForeignKey(
         Bar, related_name='+', related_query_name='foobar'
     )
     foo = models.ForeignKey(
         'app1.Foo', related_name='+', related_query_name='foobar'
     )

     class Meta:
         app_label = 'app1'

 class Foo(models.Model):
     bars = models.ManyToManyField(Bar, related_name='+',
 through='app2.FooBar')

     class Meta:
         app_label = 'app2'

 class FooBar(models.Model):
     bar = models.ForeignKey(
         Bar, related_name='+', related_query_name='foobar'
     )
     foo = models.ForeignKey(
         'app2.Foo', related_name='+', related_query_name='foobar'
     )

     class Meta:
         app_label = 'app2'
 }}}

 As you can see there's a `related_query_name='foobar'` conflict on `Bar`
 that makes `foo.bars.all()` (resolving to
 `Bar.object.filter(foobar__foo=foo`) ambiguous. This clash is not detected
 by the check framework because the
 
[https://github.com/django/django/blob/11fae7c9e4a30d88a4062eb3ee5e9fdfbcd7ba3e/django/db/models/fields/related.py#L197-L201
 relationships are hidden].

 In order to fix this I suggest we start by fixing the clash checks to run
 even if the relationship is hidden, we just have to skip `E302` and `E304`
 in this case.

 Then we should consider making implicit intermediary model creation more
 smart about this possible conflict by namespacing `related_query_name` by
 application label. This is a backward incompatible change in some way
 since users might have relied on the automatically generated queryname
 even if unlikely. The backward incompatibility could be lessened by only
 namespacing the queryname if the referenced model is from a different
 application.

--
Ticket URL: <https://code.djangoproject.com/ticket/26162#comment:2>
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 [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.846b904f98c581a27717078b43d87f4d%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to