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.
In Django, there are three main ways to write a model that represents many-to-many.
ManyToManyFieldManyToManyFieldcreated_at).through argument of ManyToManyField#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.
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).
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.
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