Для начала, изучите предыдущий урок: Создание сайта на Django: Урок 41, создание системы рейтинга статей, часть 1 (модель, представление)
Далее мы добавим ссылки нашего представления, созданного в прошлом уроке в файл urls.py нашего модуля блог.
modules/blog/urls.py
from django.urls import path, include
from modules.blog.models import Rating
from modules.blog.views import ArticleListView, ArticleDetailView, ArticleByCategoryListView, ArticleCreateView, \
ArticleUpdateView, ArticleDeleteView, CommentCreateView, ArticleByTagListView, ArticleSearchResultView, \
RatingCreateView
urlpatterns = [
path('', ArticleListView.as_view(), name='home'),
path('cat/<int:pk>/<str:slug>/', ArticleByCategoryListView.as_view(), name='article-by-cat'),
path('articles/', include([
path('', ArticleListView.as_view(), name='article-list'),
path('create/', ArticleCreateView.as_view(), name='article-create-view'),
path('tags/<str:tag>/', ArticleByTagListView.as_view(), name='article-list-by-tags'),
path('<int:pk>/comments/create/', CommentCreateView.as_view(), name='comment-create-view'),
path('<str:slug>/', ArticleDetailView.as_view(), name='article-detail'),
path('<str:slug>/update/', ArticleUpdateView.as_view(), name='article-update-view'),
path('<str:slug>/delete/', ArticleDeleteView.as_view(), name='article-delete-view'),
path('<int:pk>/like/', RatingCreateView.as_view(value=Rating.LIKE), name='article-rating-like'),
path('<int:pk>/dislike/', RatingCreateView.as_view(value=Rating.DISLIKE), name='article-rating-dislike'),
])),
path('search/', ArticleSearchResultView.as_view(), name='search'),
]
Добавили мы именно:
- path('<int:pk>/like/', RatingCreateView.as_view(value=Rating.LIKE), name='article-rating-like'),
- path('<int:pk>/dislike/', RatingCreateView.as_view(value=Rating.DISLIKE), name='article-rating-dislike'),
Пояснения:
- Мы передаем по определенной ссылке значение.
Теперь мы займемся JavaScript в файле article-list.js, его создадим по следующему пути:
templates/src/custom/js/article-list.js
ratingAll()
function ratingAll() {
document.querySelectorAll('.btn-like, .btn-dislike').forEach((e) => {
e.addEventListener('click', createRating)
})
}
function createRating(event) {
event.preventDefault()
const rating = this;
const ratingId = rating.getAttribute('data-id');
const ratingAction = rating.getAttribute('data-action');
const ratingSum = document.querySelector(`button[data-rating='${ratingId}']`);
fetch(`/articles/${ratingId}/${ratingAction}/`, {
method: 'POST',
headers: {
"X-CSRFToken": csrftoken,
"X-Requested-With": "XMLHttpRequest",
},
}).then((response) => response.json()).then((result) => {
if (result['error']) {
console.log(result['error'])
} else {
ratingSum.innerHTML = `${result['get_rating_sum']}`
}
})
}
Пояснение:
- Инициализируем функцию.
- Добавляем в кнопкам с классами .btn-like, .btn-dislike функцию по клику. На все кнопки на странице.
- В самой функции
createRating
получаем из текущего передаваемого блока this атрибуты data-id, data-action. Data-id - id статьи. Data-action - это лайк, или дизлайк. - Также передаем в кнопку с каждым id наше получаемое значение суммы рейтинга.
- Используем csrftoken в headers. Мы его использовали в этом уроке: Создание сайта на Django: Урок 32, система древовидных комментариев: часть 2 (Добавление комментариев с помощью JavaScipt, оптимизация)
- Далее данные преобразуем в json, получаем результат и выводим в консоль в случае ошибки, а если все успешно, то добавляем соотвествующий рейтинг в кнопку.
В самом шаблоне article-list.html не забываем добавить эти кнопки и подключить скрипт.
templates/modules/blog/articles/article-list.html
{% extends 'main.html' %}
{% load static %}
{% block content %}
{% for article in articles %}
<div class="card mb-3">
<div class="row">
<div class="col-md-4">
<figure class="mb-0">
<img src="{{ article.get_thumbnail }}" class="img-fluid h-100" alt="{{ article.title }}">
</figure>
</div>
<div class="col-md-8">
<div class="card-body">
<h4 class="card-title"><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></h4>
<a class="card-subtitle" href="{% url 'article-by-cat' article.category.pk article.category.slug %}">#{{ article.category }}</a> / <time>{{ article.created_at }}</time>
<p class="card-text">
{{ article.short_description|safe }}
</p>
<div class="card-text">
<button class="btn shadow-sm btn-sm btn-success btn-like p-1" data-id="{{ article.pk }}" data-action="like" type="button">+1 </button>
<button class="btn shadow-sm btn-sm btn-danger btn-dislike p-1" data-id="{{ article.pk }}" data-action="dislike" type="button">-1 </button>
<button class="btn shadow-sm btn-sm btn-primary btn-sum-rating p-1" type="button" data-rating="{{ article.pk }}">{{ article.get_rating_sum }} </button>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block script %}
<script src="{% static 'custom/js/article-list.js' %}"></script>
{% endblock %}
Проверяем на сайте, нажимаем кнопки и все отлично работает. К этим кнопкам вы можете добавить иконки соответствующих реакций, чтобы это выглядело намного лучше.

Нам нужно ещё оптимизировать эту страницу. Переходим в менеджер статей:
modules/blog/managers/articles.py
from django.db import models
class ArticleManager(models.Manager):
"""
Кастомный менеджер для модели статей.
"""
def all(self):
"""
Список статей (SQL запрос с фильтрацией для страницы списка статей)
"""
return self.get_queryset().filter(is_published=True).select_related('category').prefetch_related('article_rating')
def detail(self):
"""
Детальная страница статьи с оптимизацией
"""
return self.get_queryset().filter(is_published=True)\
.select_related('category', 'updated_by', 'author', 'author__profile')\
.prefetch_related('comments', 'comments__author', 'comments__author__profile')
Пояснение:
- К методу
def all()
добавил.prefetch_related('article_rating')
Надеюсь у вас все получилось, на этом урок закончен.