Как получить взаимных подписчиков (общего друга) для пользователей - Django
Как мне получить взаимных подписчиков (друзей друзей) с помощью request.user, которые будут отображаться для каждой публикации пользователя, я смог отобразить количество взаимных подписчиков для каждой публикации пользователя в ленте новостей. Как я могу это сделать?
Что я пробовал: мне удалось это сделать, но я не думаю, что это лучший способ, потому что, когда я использую slice для отображения изображений трех общих пользователей, это работает неправильно для всех пользователей.
Вот что я попробовал:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='UploadedProfilePicture/', default="ProfileAvatar/avatar.png", blank=True)
following = models.ManyToManyField(
'Profile', # Refers to the User model itself
symmetrical=False, # If A follows B, B doesn't automatically follow A
related_name='followers', # Reverse relationship: get followers of a user
blank=True,
)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
def following_view(request):
posts = Post.objects.filter(
Q(poster_profile__profile__followers__user=request.user)).order_by("?").distinct()
mymutual_followers = request.user.profile.following.filter(id__in=request.user.profile.following.values_list('id', flat=True))
{% for post in posts %}
{% for user_following in mymutual_followers %}
{% if user_following in post.poster_profile.profile.following.all|slice:"3" %}
<img src="{{ user_following.profile_pic.url }}" class="hover-followers-img"/>
{% endif %}
{% endfor %}
{% endfor %}
Приведенный выше код - это то, что я пробовал, и он сработал, но добавление slice:"3" для отображения только 3 изображений не работает должным образом у некоторых пользователей, поэтому я думаю, что, возможно, это не лучший подход.
Приведенный ниже код показывает, как я получил количество взаимных подписчиков, и он отлично работает, но не может отобразить изображения взаимных подписчиков; как я могу это сделать?
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='UploadedProfilePicture/', default="ProfileAvatar/avatar.png", blank=True)
following = models.ManyToManyField(
'Profile', # Refers to the User model itself
symmetrical=False, # If A follows B, B doesn't automatically follow A
related_name='followers', # Reverse relationship: get followers of a user
blank=True,
)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
def following_view(request):
posts = (
Post.objects.filter(Q(poster_profile__profile__followers__user=request.user)).order_by("?")
.annotate(
mutual_count=Count(
'poster_profile', filter=Q(poster_profile__profile__following__followers__user=request.user)
) # Got the count for mutual followers
)
.prefetch_related( # what am I doing wrong here....
Prefetch(
'poster_profile',
Post.objects.annotate(
is_mutual=Exists(
Post.objects.filter(
poster_profile=OuterRef('pk'),
poster_profile__profile__followers__user=request.user,
)
)
).filter(is_mutual=True),
to_attr='mutual_followers',
)
)
)
# Template
{% for post in posts %}
{{ post.mutual_count }} # Got mutual count
{% endfor %}
# This is not displaying images.
{% for post in posts %}
{% for img in post.mutual_followers %}
<img src="{{ img.profile_pic.url }}" class="hover-followers-img"/>
{% endfor %}
{% endfor %}
Как я понимаю, вы уже подсчитываете количество взаимных подписчиков, но все, что вы хотите, - это отобразить фактические объекты взаимных подписчиков для каждого поста. Причина, по которой ваш первоначальный подход с annotate и prefetch_related не работает, заключается в том, что, хотя annotate хорош для подсчета, он не извлекает связанные объекты. Между тем, prefetch_related с Prefetch в основном предназначен для ManyToMany или обратных ForeignKey отношений. Это ничего не делает для прямого ForeignKey типа poster_profile.
Выполнение этого непосредственно в вашем шаблоне с помощью чего-то похожего на: {% if user_following in post.poster_profile.profile.following.all|slice:"3" %}, скорее всего, приведет к множеству дополнительных запросов к базе данных и в целом будет довольно запутанным.
Я думаю, что хороший способ справиться с этим - создать список взаимных подписчиков прямо в вашем представлении! Затем сократите его до 3 лучших подписчиков (или сколько бы вы их ни выбрали) и перенесите все в уже готовый шаблон. Это позволяет просматривать список взаимных подписчиков для каждой публикации и избежать тех медленных запросов N+1, которые возникают при нарезке или фильтрации чего-либо в шаблоне.
Вот как вы могли бы структурировать свое представление:
def following_view(request):
user_profile = request.user.profile
posts = Post.objects.filter(
poster_profile__profile__followers__user=request.user
).select_related('poster_profile', 'poster_profile__profile').distinct()
post_data = []
for post in posts:
poster_profile = post.poster_profile.profile
mutual_followers_qs = user_profile.following.filter(
id__in=poster_profile.following.values_list('id', flat=True)
)
post_data.append({
'post': post,
'mutual_followers': mutual_followers_qs[:3], # You can select any number of followers here
'mutual_count': mutual_followers_qs.count()
})
return render(request, "your_app_here/newsfeed.html", {"post_data": post_data})
А затем обновите свой шаблон до чего-то вроде этого:
{% for item in post_data %}
<div>
<p>Post by {{ item.post.poster_profile.username }}</p>
<p>{{ item.mutual_count }} mutual followers</p>
<div>
{% for mutual_follower in item.mutual_followers %}
<img src="{{ mutual_follower.profile_pic.url }}" alt="mutual follower" class="hover-followers-img">
{% empty %}
<span>No mutual followers yet</span>
{% endfor %}
</div>
</div>
{% endfor %}
Вся логика остается в поле вашего зрения, поэтому вы избегаете медленных циклов создания шаблонов и ненужной нарезки, а также гарантированно получаете взаимных подписчиков на каждый пост.