Django 5 для начинающих

Прогресс по курсу:  9/1004

11.6 Создание системы Like / Dislike
3 из 3 шагов пройдено

В этом разделе мы рассмотрим создание системы Лайк / Дизлайк в Django без перезагрузки страницы с помощью JavaScript.

Создание модели рейтинга

Первым делом мы создадим модель рейтинга, для этого в файле models.py приложения blog добавим код модели:

class Rating(models.Model):
    """
    Модель рейтинга: Лайк - Дизлайк
    """
    post = models.ForeignKey(to=Post, verbose_name='Запись', on_delete=models.CASCADE, related_name='ratings')
    user = models.ForeignKey(to=User, verbose_name='Пользователь', on_delete=models.CASCADE, blank=True, null=True)
    value = models.IntegerField(verbose_name='Значение', choices=[(1, 'Нравится'), (-1, 'Не нравится')])
    time_create = models.DateTimeField(verbose_name='Время добавления', auto_now_add=True)
    ip_address = models.GenericIPAddressField(verbose_name='IP Адрес')

    class Meta:
        unique_together = ('post', 'ip_address')
        ordering = ('-time_create',)
        indexes = [models.Index(fields=['-time_create', 'value'])]
        verbose_name = 'Рейтинг'
        verbose_name_plural = 'Рейтинги'

    def __str__(self):
        return self.post.title

Этот код определяет модель Rating, которая наследуется от models.Model из Django. Модель содержит следующие поля:

  • post - поле внешнего ключа, которое ссылается на модель Post.
  • user - поле внешнего ключа, которое ссылается на модель User. Оно может быть пустым, так мы планируем разрешить гостям ставить лайк и дизлайк.
  • value - поле выбора значений, которое может иметь одно из двух значений: 1(Нравится) или -1(Не нравится).
  • time_create - поле даты и времени, которое устанавливается автоматически при создании объекта.
  • ip_address - поле IP-адреса.

Модель определяет несколько метаданных для управления ее поведением:

  • unique_together гарантирует уникальность комбинации  post и ip_address.
  • ordering указывает порядок сортировки по умолчанию.
  • indexes определяет индекс на поля time_create и value.
  • verbose_name и verbose_name_plural определяют отображаемые имена модели в административном интерфейсе.

Не забываем провести миграции:

python manage.py makemigrations
python manage.py migrate

 

Подсчет рейтинга у записи

Теперь нам необходимо написать функцию подсчета суммы рейтинга, для этого у модели Post добавим следующий метод:

class Post(models.Model):

   # поля и методы модели

    def get_sum_rating(self):
        return sum([rating.value for rating in self.ratings.all()])

 

Создаем представление для работы с рейтингом

Нам необходимо создать представление, которое будет работать с JS, для этого в нашем файле views.py приложения blog добавим следующее представление:

from django.http import JsonResponse
from django.views.generic import View
from .models import Rating


class RatingCreateView(View):
    model = Rating

    def post(self, request, *args, **kwargs):
        post_id = request.POST.get('post_id')
        value = int(request.POST.get('value'))
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        ip = x_forwarded_for.split(',')[0] if x_forwarded_for else request.META.get('REMOTE_ADDR')
        ip_address = ip
        user = request.user if request.user.is_authenticated else None

        rating, created = self.model.objects.get_or_create(
           post_id=post_id,
            ip_address=ip_address,
            defaults={'value': value, 'user': user},
        )

        if not created:
            if rating.value == value:
                rating.delete()
                return JsonResponse({'status': 'deleted', 'rating_sum': rating.post.get_sum_rating()})
            else:
                rating.value = value
                rating.user = user
                rating.save()
                return JsonResponse({'status': 'updated', 'rating_sum': rating.post.get_sum_rating()})
        return JsonResponse({'status': 'created', 'rating_sum': rating.post.get_sum_rating()})

Этот код представляет класс-представление, который обрабатывает POST- запросы на создание и изменение модели Rating. Он использует стандартный метод post() для обработки запроса.

        post_id = request.POST.get('post_id')
        value = int(request.POST.get('value'))

При получении запроса, представление извлекает данные из запроса, включая идентификатор статьи, значение оценки, IP-адрес клиента и аутентифицированного пользователя (если таковой имеется).

Затем используется метод get_or_create() для получения экземпляра модели Rating. Если объект уже существует, он либо удаляется, либо обновляется соответствующим образом, а если объект новый, он сохраняется.

Наконец, метод возвращает JsonResponse, содержащий статус операции (created, updated или deleted) и сумму оценок для записи, полученных с помощью метода get_sum_rating(), определенного в модели Post.

 Добавим нашу модель в админ панель, для этого в файл admin.py приложения blog добавим следующий код:

from .models import Rating

@admin.register(Rating)
class RatingAdmin(admin.ModelAdmin):
    """
    Админ-панель модели рейтинга
    """
    pass

В следующем шаге мы добавим маршруты и JS-скрип обработчика этой системы.


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

В системе идентификациии пользователя по IP есть 2 изъяна, 1) войдя в свой акаунт с другого устройства IP будет другой, можно проголосавать еще раз, и если у тебя есть два акаунта, но ты заходишь с одного устройства учитывается только 1 голос. Почему бы сразу не учитывать по id пользователя голосовал он или нет ?

С другой стороны можно использовать функцию получения IP для другого, например вести статистику посищений сайта.

@Владислав_Финогенов, У нас так сделано чтобы и гости могли голосовать. Тут уже полет фантазии, можно реализовать любую логику работы.

@Илья_Перминов

У меня сейчас возникают проблемы с голосованием с таким решением причину не нашел, в общем спустя какое то время при голосовании ничего не меняется, не могу поставить лайк или дизлайк, один раз прокатило очистить весь кэш в браузере, а потом не прокатило. Пока в поисках проблемы 

@Владислав_Финогенов, даже не знаю что подсказать, у меня таких проблем не было. А какой браузер используете и его версию?

@Илья_Перминов

Вечерком продебажу, раскидаю принты и проверю в б.д что записано, может быть найду что то

@Илья_Перминов

Косяков ни каких не нашел, но заработало все после жесткой перезагрузке страницы, может из за браузера(Chrome) хз.