#26352: models.E003 check incorrectly prevents duplicate ManyToMany through-self
that differ by through_fields
-------------------------------------+-------------------------------------
               Reporter:  simonw     |          Owner:  nobody
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  1.9
  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          |
-------------------------------------+-------------------------------------
 Let's say we're building a Twitter-style friendship model, where a user
 can follow other users, and can have users that follow them.

 A sensible way to model that might look like this:

 {{{#!python
 class User(models.Model):
     friends = models.ManyToManyField(
         'self', through='Followship', symmetrical=False,
         through_fields=('user', 'target'), related_name='+'
     )
     followers = models.ManyToManyField(
         'self', through='Followship', symmetrical=False,
         through_fields=('target', 'user'), related_name='+'
     )

 class Followship(models.Model):
     user = models.ForeignKey(User, models.CASCADE, related_name='+')
     target = models.ForeignKey(User, models.CASCADE, related_name='+')
 }}}

 Here we have an intermediary table called "Followship", which relates a
 user to the target user that they are following.

 We also have two helpful convenience ManyToMany fields defined on the
 user: `user.friends.all()` returns all other users that the user is
 following, while `user.followers.all()` returns the users that are
 following our current user.

 The above models should work fine... but they don't, because Django's
 model checking framework throws the following error:

     users.User: (models.E003) The model has two many-to-many relations
 through the intermediate model 'users.Followship'.

 I don't think this warning is warranted in this case, because the
 `through_fields` arguments provided to the friends/followers
 ManyToManyFields mean that the relationships defined here make sense and
 the models should work correctly.

 I've tested this theory by disabling the warning entirely using an over-
 ride class method on user, like this:

 {{{#!python
 class User(models.Model):
     friends = models.ManyToManyField(
         'self', through='Followship', symmetrical=False,
         through_fields=('user', 'target'), related_name='+'
     )
     followers = models.ManyToManyField(
         'self', through='Followship', symmetrical=False,
         through_fields=('target', 'user'), related_name='+'
     )

     @classmethod
     def _check_m2m_through_same_relationship(cls):
         # Disable models.E003 check for this model
         return []
 }}}

 This does the trick: the check is suppressed, and the models work as
 expected.

 I think the model check framework is being overly strict here. I think it
 should be modified to enable this pattern, provided the through_fields=
 parameters on the duplicate ManyToMany fields are present and
 differentiate the fields correctly.

--
Ticket URL: <https://code.djangoproject.com/ticket/26352>
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/049.af60830ff4c5fbb47f3d66ae1c30ef18%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to