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.
ManyToManyField
ManyToManyField
created_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 is
models.CASCADEbecause these relationships should also be deleted when the user is deleted. If this deletes the target data of
Foreignkey`, this data will also be deleted automatically.
related_name
If you use multiple Foreignkey
s 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