Создание модели системы лайк/дизлайк в Django 4.1
Напоминаю, что я использую в своем проекте декомпозицию, поэтому я создам файл модели rating.py в modules/blog/models

Приступим к созданию модели:
modules/blog/models/rating.py
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
from modules.blog.models import Article
class Rating(models.Model):
LIKE = 1
DISLIKE = -1
VOTES_TYPE = ((LIKE, _('Нравится')), (DISLIKE, _('Не нравится')))
article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name='Статья', related_name='article_rating')
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Оценил'), related_name='article_rating_author', blank=True, null=True)
created_at = models.DateTimeField(verbose_name=_('Время добавления оценки'), auto_now_add=True, db_index=True)
value = models.SmallIntegerField(verbose_name=_('Оценка'), choices=VOTES_TYPE)
ip_address = models.GenericIPAddressField(verbose_name=_('IP Адрес'), blank=True, null=True)
class Meta:
"""
Сортировка, название модели в админ панели, таблица в данными
"""
ordering = ('-created_at',)
verbose_name = _('Рейтинг материала')
verbose_name_plural = _('Рейтинг материалов')
db_table = 'app_ratings'
def __str__(self):
if self.author:
return f'{self.author} поставил {self.get_value_display()} под {self.article.title}'
return f'Гость: {self.ip_address} поставил {self.get_value_display()} под {self.article.title}'
Эту модель я позаимствовал с сайта evileg.com, от создателя Евгения Легоцкого.
Пояснения:
- У нас есть значение value, которое принимает 1 или -1 и создает запись к определенной статей, под определенным автором, или же гостем.
- ip_address мы используем для гостей, как сбор статистики для себя.
Не забываем, если вы используете структуру файлов такую же, как у меня, то нужно модель добавить в __init__.py
modules/blog/models/__init__.py
from modules.blog.models.articles import Article
from modules.blog.models.categories import Category
from modules.blog.models.comments import Comment
from modules.blog.models.rating import Rating
__all__ = ('Article', 'Category', 'Comment', 'Rating')
Отлично, проведем миграции:
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py makemigrations
Migrations for 'blog':
modules\blog\migrations\0007_rating.py
- Create model Rating
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions, sites, system, taggit
Running migrations:
Applying blog.0007_rating... OK
Теперь нам нужно создать представление для будущего использования в JavaScript. Создам файл представления rating.py во views
modules/blog/views/rating.py
class RatingCreateView(View):
value = None
def is_ajax(self):
return self.request.headers.get('X-Requested-With') == 'XMLHttpRequest'
def post(self, request, pk):
if self.is_ajax():
current_user = request.user
ip_address = get_client_ip(request)
article = Article.objects.get(pk=pk)
if current_user.is_authenticated:
try:
rating = article.article_rating.get(author=current_user)
if rating.value is not self.value:
rating.value = self.value
rating.save(update_fields=['value'])
else:
rating.delete()
except ObjectDoesNotExist:
article.article_rating.create(author=current_user, value=self.value)
return JsonResponse({
'get_rating_sum': article.get_rating_sum()
}, status=200)
else:
try:
rating = article.rating.get(ip_address=ip_address)
if rating.value is not self.value:
rating.value = self.value
rating.save(update_fields=['value'])
else:
rating.delete()
except ObjectDoesNotExist:
article.article_rating.create(ip_address=ip_address, value=self.value)
return JsonResponse({
'get_rating_sum': article.get_rating_sum()
}, status=200)
Пояснения:
- Используем просто View, инициализируем значение, чтоб его можно было передать из urls.py и шаблона, и применить.
- Используем POST запрос только в режиме ajax/fetch
- Я думаю, код можно вполне оптимизировать и написать лучше и меньше. Но на вечер мне было заниматься этим лень...
Добавим новое представление в __init__.py
modules/blog/views/__init__.py
from modules.blog.views.articles import *
from modules.blog.views.comments import *
from modules.blog.views.rating import *
__all__ = '__all__'
Давайте ещё создадим метод в нашей модели Article:
modules/blog/models/articles.py
def get_rating_sum(self):
"""
Подсчет рейтинга для статей
"""
return sum([rating.value for rating in self.article_rating.all()])
В следующей части мы создадим JavaScript в шаблоне, чтобы добавлять рейтинг без обновления страницы.