Проблема с добавлением объектов во многие поля
У меня есть модель пользователя:
class User(AbstractUser):
followers_num = models.IntegerField(default=0)
followings_num = models.IntegerField(default=0)
followers = models.ManyToManyField('self', blank=True, symmetrical=False)
followings = models.ManyToManyField('self', blank=True, symmetrical=False)
И есть представление для добавления/удаления объектов пользователей из последователей/фолловеров:
def follow(request, follow, user):
if (request.method == 'PUT'):
following_user = User.objects.get(username=user)
follower_user = User.objects.get(username=request.user.username)
if follow:
# Follow
following_user.followers.add(follower_user)
following_user.followers_num += 1
follower_user.followings.add(following_user)
follower_user.followings_num += 1
else:
# Unfollow
following_user.followers.remove(follower_user)
following_user.followers_num -= 1
follower_user.followings.remove(following_user)
follower_user.followings_num -= 1
following_user.save()
follower_user.save()
return HttpResponse(status=204)
Я хочу, чтобы он добавил follower_user к последователям following_user и добавил following_user к последователям follower_user. Однако вместо этого он добавляет follower_user не только к последователям following_user, но и к последователям following_user. Почему так происходит?
Редактирование:
Я добавил symmetrical=False к полям, но это вызвало ошибку:
SystemCheckError: System check identified some issues:
ERRORS:
network.User.followers: (fields.E304) Reverse accessor for 'network.User.followers' clashes with reverse accessor for 'network.User.followings'.
HINT: Add or change a related_name argument to the definition for 'network.User.followers' or 'network.User.followings'.
network.User.followings: (fields.E304) Reverse accessor for 'network.User.followings' clashes with reverse accessor for 'network.User.followers'.
HINT: Add or change a related_name argument to the definition for 'network.User.followings' or 'network.User.followers'.
Используйте это в качестве модели:
class User(AbstractUser):
followers_num = models.IntegerField(default=0)
followings_num = models.IntegerField(default=0)
followers = models.ManyToManyField('self', related_name='followings', null=True, blank=True)
В этом случае используется логика symmetrical, о которой упоминал Виллем. Это позволит вам избежать необходимости отслеживать отношения в двух ManyToManyFields.
Однако вместо этого он добавляет follower_user не только к
followersизfollowing_user, но и добавляет его кfollowingsизfollowing_user. Почему это происходит?
Because relations in Django to the self are by default symmetric. You should make the relation asymmetric by setting symmetrical=False [Django-doc] and simply use one ManyToManyField, so:
class User(AbstractUser):
followers = models.ManyToManyField(
'self',
blank=True,
symmetrical=False,
related_name='following'
)
Логика проста:
from django.shortcuts import get_object_or_404
def follow(request, follow, user):
if request.method == 'PUT':
following_user = get_object_or_404(User, username=user)
follower_user = request.user
if follow:
following_user.followers.add(follower_user)
else:
following_user.followers.remove(follower_user)
return HttpResponse(status=204)
Если пользователь A находится в .followers из B, то B автоматически находится в .following из A, поэтому нет необходимости использовать два отношения.
Более того, вам не обязательно следить за количеством последователей, вы можете подсчитать это с помощью .annotate(…) [Django-doc] при необходимости.
Note: It is often better to use
get_object_or_404(…)[Django-doc], then to use.get(…)[Django-doc] directly. In case the object does not exists, for example because the user altered the URL themselves, theget_object_or_404(…)will result in returning a HTTP 404 Not Found response, whereas using.get(…)will result in a HTTP 500 Server Error.