Создание сайта на Django: Урок 41, создание системы рейтинга статей, часть 1 (модель, представление)
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 0

Создание модели системы лайк/дизлайк в 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 в шаблоне, чтобы добавлять рейтинг без обновления страницы. 

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)