Обновленная версия системы подписчиков
Вывод подписчиков в шаблоне Django
Напоминаю, что в проекте я использую декомпозицию.
Модель профиля мы создавали, ознакомьтесь с ней в этом уроке Создание сайта на Django: Урок 19, профиль пользователя в Django: часть 1 (модель, сигналы)
Мы добавили следующее поле к профилю пользователя: following = models.ManyToManyField('self', verbose_name='Подписки', related_name='followers', symmetrical=False, blank=True)
Пояснения:
- Это поле многое ко многим (m2m).
related_name
задал как followerssymmetrical
- отвечает за то, чтоб подписка происходила в одностороннем порядке.
Давайте добавим вывод в шаблон подписчиков, а также добавим их через админку.
Создам пару профилей, и подпишусь с моего аккаунта на них:

А теперь перейду в шаблон профиля и добавлю следующие шаблонные теги для вывода подписчиков:
templates/modules/system/profiles/profile-detail.html
{% extends 'main.html' %}
{% block content %}
<div class="card border-0 mb-2">
<div class="card-body">
<div class="row">
<div class="col-md-3">
<figure>
<img src="{{ profile.get_avatar }}" class="img-fluid rounded-0" alt="{{ profile }}">
</figure>
</div>
<div class="col-md-9">
<h5 class="card-title">
{{ profile }}
</h5>
<div class="card-text">
<ul>
<li>Никнейм: {{ profile.user.username }}</li>
<li>Заходил: {{ profile.user.last_login }}</li>
<li>Возраст: {{ profile.get_age }}</li>
<li>О себе: {{ profile.bio }}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6 class="card-title">
Подписки
</h6>
<div class="card-text">
<div class="row">
{% for following in profile.following.all %}
<div class="col-md-2">
<a href="{{ following.get_absolute_url }}">
<img src="{{ following.get_avatar }}" class="img-fluid rounded-1" alt="{{ following }}"/>
</a>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="col-md-6">
<h6 class="card-title">
Подписчики
</h6>
<div class="card-text">
<div class="row" id="followersBox">
{% for follower in profile.followers.all %}
<div class="col-md-2" id="user-slug-{{ follower.slug }}">
<a href="{{ follower.get_absolute_url }}">
<img src="{{ follower.get_avatar }}" class="img-fluid rounded-1" alt="{{ follower }}"/>
</a>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Пояснения:
- Я добавил вывод подписчиков и подписки на других пользователей
- id
followersBox
понадобится для javascript. - Передавать мы будем slug для JS
Как это выглядит на сайте:

Добавление представления системы подписок
В представления профиля добавлю следующий фрагмент кода:
modules/system/views/profiles.py
from django.views import View
from django.http import JsonResponse
class ProfileFollowingCreateView(View):
"""
Создание подписки для пользователей
"""
model = Profile
def is_ajax(self):
return self.request.headers.get('X-Requested-With') == 'XMLHttpRequest'
def post(self, request, slug):
if self.is_ajax():
user = self.model.objects.get(slug=slug)
current_user = request.user
if current_user.is_authenticated:
profile = current_user.profile
if profile in user.followers.all():
user.followers.remove(profile)
return JsonResponse({
'author': current_user.username,
'following_avatar': profile.get_avatar,
'following_get_absolute_url': profile.get_absolute_url(),
'following_slug': profile.slug,
'message': f'Подписаться на {user}',
'status': False},
status=200)
else:
user.followers.add(profile)
return JsonResponse({
'author': current_user.username,
'following_get_absolute_url': profile.get_absolute_url(),
'following_slug': profile.slug,
'following_avatar': profile.get_avatar,
'message': f'Отписаться от {user}',
'status': True},
status=200)
else:
return JsonResponse({'error': 'Необходима авторизация на сайте'}, status=400)
Пояснения:
- Это представление будет работать только в JS.
- Логика довольно проста, если текущего пользователя нет в подписка пользователя, он добавляется, иначе - удаляется.
- Передаем данные после выполнения в JsonResponse.
Добавим наше представление в urls.py
modules/system/urls.py
from django.urls import path
from modules.system.views import ProfileView, ProfileEditView, RegisterCreateView, UserLoginView, \
UserPasswordChangeView, UserForgotPasswordView, UserPasswordResetConfirmView, UserLogoutView, ActivateAccountView, \
FeedbackCreateView, ProfileFollowingCreateView
urlpatterns = [
path('user/edit/', ProfileEditView.as_view(), name='profile-edit'),
path('user/<str:slug>/', ProfileView.as_view(), name='profile'),
path('user/<str:slug>/follow/', ProfileFollowingCreateView.as_view(), name='follow-to-user'),
path('register/', RegisterCreateView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('logout/', UserLogoutView.as_view(), name='logout'),
path('password-change/', UserPasswordChangeView.as_view(), name='password-change'),
path('password-reset/', UserForgotPasswordView.as_view(), name='password-reset'),
path('set-new-password/<uidb64>/<token>/', UserPasswordResetConfirmView.as_view(), name='password-reset-confirm'),
path('activate/<uidb64>/<token>/', ActivateAccountView.as_view(), name='activate'),
path('feedback/', FeedbackCreateView.as_view(), name='feedback-create-view'),
]
На этом урок закончен, в следующем уроке мы уже просто оформим работу системы подписок через JavaScript, добавив необходимые кнопки.