[PYTHON] Create an easy-to-use follow model in Django using ManyToManyField through

Recently, when I made a model for the follow function again with Django, I tried using the through argument and found a model that is easy to write, so I wrote it as an article.

I haven't written it in a particularly advanced way, but when I searched for the follow function, I couldn't find a model like this, so I hope it will be helpful for those who create similar functions.

How to write many-to-many in Django

In Django, there are three main ways to write a model that represents many-to-many.

What you can do

#follower of user1/Get all followers
user1.followers.all()
user1.followees.all()

#Item list of users that user1 is following(Timeline on Twitter)Get
Item.objects.filter(user__followers=user1)

#user1 follows user2
user1.followee_friendships.create(followee=user2)

You will be able to write like this.

Implementation

Intermediate model

class FriendShip(models.Model):
    follower = models.ForeignKey('User', on_delete=models.CASCADE, related_name='followee_friendships')
    followee = models.ForeignKey('User', on_delete=models.CASCADE, related_name='follower_friendships')

    class Meta:
        unique_together = ('follower', 'followee')

An intermediate model that represents follow relationships. ʻForeign key is directed to each as a follower and follower for the User model.

The ʻon_deleteargument ismodels.CASCADEbecause these relationships should also be deleted when the user is deleted. If this deletes the target data ofForeignkey`, this data will also be deleted automatically.

related_name If you use multiple Foreignkeys for the same class, you must set related_name or you will get an error at the time of the manage.py make migrations command. By setting this, the foreign key connection destination can refer to this model in the opposite direction.

Here follower_friendships behaves like ʻobjects`. For example, the following two lines have the same meaning.

Friendship.objects.create(follower=user1, followee=user2)
user1.followee_friendships.create(followee=user2)

These represent follow-ups from ʻuser1 to ʻuser2.

unique_together This is to set a constraint that the combination is unique.

There is always one combination of followers for followers, and creating multiple followers is an abnormal behavior, so set this.

In addition, it seems that there is no error even if there is a follow and follow that are reversed (so-called mutual follow).

User model

class User(AbstractUser):
    followees = models.ManyToManyField(
        'User', verbose_name='Users following', through='FriendShip',
        related_name='+', through_fields=('follower', 'followee')
    )
    followers = models.ManyToManyField(
        'User', verbose_name='Users being followed', through='FriendShip', 
        related_name='+', through_fields=('followee', 'follower')
    )

AbstractUser ʻAbstractUser is for extending the default User. ʻUsed when overwriting or adding a new field that User already has.

This already has some better information, so see the commentary or official documentation for more details.

ManyToManyField The FriendShip model is specified in the through argument. Now all you have to do is call followees, followers and you'll get the data through the intermediate model.

related_name Specifying + indicates that no back reference is required. The documentation says:

If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'. For example, this will ensure that the User model won’t have a backwards relation to this model:

https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ForeignKey.related_name

through_fields When targeting one data and another data associated with it in ManyToManyField as a source and target, pass(source, target)in the through_fields argument.

ʻUser.followee wants to get the follow of a user, so we specify ('follower','followee') , and ʻuser.follower specifies the opposite.

Digression

If you specify a back reference with related_name, you only need oneManyToManyField.

class User(AbstractUser):
    followers = models.ManyToManyField(
        'User', verbose_name='Users being followed', through='FriendShip', 
        related_name='followees', through_fields=('followee', 'follower')
    )

However, there may be inconveniences such as not being able to specify verbose_name, so it seems better to declare two.

Recommended Posts

Create an easy-to-use follow model in Django using ManyToManyField through
Django model: ManyToManyField
Precautions when adding items using DateField to an existing model later in Django
[Python] Create an infrastructure diagram in 3 minutes using diagrams
Implement follow functionality in Django
Create an API with Django
Create an authentication feature with django-allauth and CustomUser in Django
Show Django ManyToManyField in Template
Create an API that returns data from a model using turicreate
Deploy Django in 3 minutes using docker-compose
Create a LINE Bot in Django
I want to create an API that returns a model with a recursive relationship in the Django REST Framework
Django: Migration doesn't reflect model in DB
WEB application development using Django [Model definition]
Information recording memo using session in Django
CSS environment created in 10 minutes using Django
Create a model for your Django schedule
Create an update screen with Django Updateview
Create an application using the Spotify API
Ajax in Django (using generic class view)
Get a reference model using Django Serializer
Benefits of using slugfield in Django's model
Implement a Custom User Model in Django
Create an elliptical scatter plot in Python without using a multivariate normal distribution
If you want to display values using choices in a template in a Django model
Create a REST API using the model learned in Lobe and TensorFlow Serving.