﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
26352	models.E003 check incorrectly prevents duplicate ManyToMany through-self that differ by through_fields	Simon Willison	nobody	"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."	Bug	closed	Core (System checks)	1.9	Normal	fixed			Accepted	1	0	0	1	0	0
