Комментарии с помощью contentype
Всем доброго времени суток! Сегодня я расскажу, как делал свою систему коментариев к интернет магазину. Сразу скажу, что здесь приведен не весь код а его часть т.к. основная логика обработки функций является коммерческой.
На сайте комментарии должны создваться для нескольких разделов это Новости и Продукт. Думаю не правильно было бы писать комментарии как для одного раздела так и для другого. Логично сделать одно приложение "комментарии" и подключать его к другим приложениям к которым это нужно.
Приступим.
Создаем новое прложение Comments и добавляем его в массив installed_apps вашего проекта.
Пишем модель комментариев, в моем случае я сделал так.
class Comment(MPTTModel):
parent = TreeForeignKey('self', blank=True, null=True, verbose_name='Родительская категория', related_name='children', on_delete=models.CASCADE, editable=False) # editable=False (Скрыл поле parent в админке)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE, verbose_name='Пользователь')
text = models.TextField(verbose_name='Комментарий')
email = models.EmailField(max_length=50, blank=True, verbose_name='e-mail')
user_name = models.CharField(max_length=100, blank=True, verbose_name='Логин пользователя')
like = models.IntegerField(default=0, verbose_name='like')
dislike = models.IntegerField(default=0, verbose_name='dislike')
user_like = models.ManyToManyField(User, verbose_name='Кто поставил лайк', related_name='users_like', blank=True)
user_dislike = models.ManyToManyField(User, verbose_name='Кто поставил дизлайк', related_name='users_dislike', blank=True)
count_comment = models.IntegerField(default=0, verbose_name='Количество комментариев')
is_active = models.BooleanField(default=False, verbose_name='Модерация')
created = models.DateTimeField(auto_now_add=True, verbose_name='Создан')
updated = models.DateTimeField(auto_now=True, verbose_name='Обновлен')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # Создаем связь с внешней моделью ContentType.
object_id = models.PositiveIntegerField() # Содаем поле, которое будет хранить значения первичных ключей экземпляров модели с которой создана связь.
content_object = GenericForeignKey('content_type', 'object_id') # В это поле передаем в качестве аргументов, имена полей созданных ранее.
class Meta:
verbose_name = 'Комментарий'
verbose_name_plural = 'Коментарии'
ordering = ['-created']
def __str__(self):
return '{}'.format(self.content_object)
Как видно в модели Comment нет связи ForeignKey на внешнюю модель в которой мы бы хотели выводить комментарии, в моем случае это модель News и Product. Вместо этого у нас есть связь на промежуточную модель ContentType. Вот об этом нужно поговорить подробнее.
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # Создаем связь с внешней моделью ContentType.
object_id = models.PositiveIntegerField() # Содаем поле, которое будет хранить значения первичных ключей экземпляров модели с которой создана связь.
content_object = GenericForeignKey('content_type', 'object_id') # В это поле передаем в качестве аргументов, имена полей созданных ранее.
В моедле я создал три поля, эти поля подписаны комментариями. Если кратко, то Content Types Framework сохраняет информацию о моделях в таблицу contenttypes, а именно: название приложения, название модели и тип.
Это говорит о том, что таким образом можно создать GenericForeignKey на любую модель используя всего одно поле.
Нужно не забыть в моделях в которых будут выводится комментарии сделать обратную ссылку на комментарии comments = GenericRelation('comments.comment')
В моем случае я добавил это поле в модель News в приложении info и модель Product в приложении shop. Позже объясню для чего это нужно будет.
Далее создаем templatetags, как это сделать я писал в этой статье. Создаем в проекте каталог templatetags в нем создаем файл __init__.py - говорит что это модуль питона и создаем сам модуль comments.py .
from django import template
from django.core.paginator import Paginator
from django.contrib.contenttypes.models import ContentType
from comments.forms import CommentForm, CommentFormCaptcha
register = template.Library()
@register.inclusion_tag('add_comments.html')
def add_comment(obj, request):
'''Создаем темплейттег'''
'''Создание словаря для инициализации формы начальными значениями'''
form_initial = {'content_type': ContentType.objects.get_for_model(obj), 'object_id': obj.pk}
'''Получаем комментарии связанные с объектом'''
comments = obj.comments.filter(is_active=True).order_by('-created')
'''Счетаем общее количество коментариев к посту'''
all_comment = comments.count()
'''Пагинация комментариев'''
paginator = Paginator(comments, 5)
page = request.GET.get('page')
comments = paginator.get_page(page)
'''Проверяем, если пользователь не авторизован то выводим форму с капчей иначе если авторизован, то выводим форму без капчи'''
if not request.user.is_authenticated:
form = CommentFormCaptcha(initial=form_initial) # Форма с капчей
else:
form = CommentForm(initial=form_initial) # Форма без капчи
return {'comments': comments,
'all_comment': all_comment,
'form': form,
'request': request,
}
Впринципе тут особо нечего описывать не нужно т.к. все и так понятно и подписано в самом коде. Сотит отметить, что шаблон для этого темплейттега я сделал "add_comments.html" в нем будет осуществляться создание и вывод комментариев.
Дальше в forms.py создаем форму комментариев, в urls.py создаем маршрутизацию, в views.py создаем функцию которая будет создавать комментарий, функцию редактирования комментария, функцию удаления комментария и функции лайка и дизлайка. Подробно я не буду описывать это все т.к. это страндарно.
Чтоб отобразить комментарии нужно в шаблоне приложения где вы хотите вывести комментарии подгрузить модуль темплейттега {% load comments %} и вывести сам темплейттег в моем случае для новостей {% add_comment newsdetails request %} и для продукта {% add_comment product request %}
Что бы посчетать количество комметарев к статье, нужно задействовать нашу обратную ссылку о которой я писал ренее на модель комментариев. Здесь мы добираемся до модели комментариев all_comment = newsdetails.comments.count() .
Конечно это не полная статья а больше как заметка для себя, если у вас будут какие либо вопросы, то вы всегда можете написать мне на емейл или через форму обратной связи.
Ваши комментарии
Комментарии могут оставлять только зарегистрированые пользователи!