Ben Beatles Levine
Ben Beatles Levine

Reputation: 153

Many-to-many relationship with itself can't utilize the related name attribute

I am trying to make a Twitter clone.

The app has a series of users, each of which has a user profile. The UserProfile model is as follows

class UserProfiles(models.Model):
    authenticated_user = models.OneToOneField(User, related_name="user_profile")

    handle = models.CharField(max_length=50)
    display_name = models.CharField(max_length=50)

    following = models.ManyToManyField("self", related_name="followers", null=True)

    def __str__(self):
        return self.authenticated_user.handle

The "following" attribute is a many-to-many relationship with UserProfiles, as each user profile can follow many other profiles, and many profiles can be following another profile.

If I have an instance of UserProfiles, we'll call it current_user, I can find how many profiles it's following by doing

current_user.following.count

I also want to be able to tell the user how many people are following them. Since the related_name argument is set to "followers," it seems reasonable that I can get a count of followers by doing

current_user.followers.count

This, however, returns an error, "'UserProfiles' object has no attribute 'followers'"

Why won't what I did work? What can I do instead?

Upvotes: 2

Views: 517

Answers (1)

AKS
AKS

Reputation: 19861

The default m2m relation to self is symmetrical in django. If you look at the documentation:

When Django processes this model, it identifies that it has a ManyToManyField on itself, and as a result, it doesn’t add a person_set attribute to the Person class. Instead, the ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.

If you do not want symmetry in many-to-many relationships with self, set symmetrical to False. This will force Django to add the descriptor for the reverse relationship, allowing ManyToManyField relationships to be non-symmetrical.

In your case since symmetrical attribute for the m2m relationship is true, it means:

A is following B => B is following A

But if you need both following and followers relationships you need to set symmetrical=False:

following = models.ManyToManyField("self", related_name="followers", 
                                   symmetrical=False, null=True)

Upvotes: 2

Related Questions