- Не знаешь как реализовать комментирование на своём сайте ?
- Ищешь возможности и варианты реализации для этого самого комментирования ?
- Просто хочешь готовое приложение/решение которое можно было бы легко интегрировать на свой собственный сайт ?
Комментарии на сайт используя Django
11.11.2023
108
0
0
0
0
Вступление
Привет. Если ты, как и я, находишься на том этапе что:
Тогда ты попал по-верному URL-адресу.
В этой статье я опишу то как можно реализовать простую систему комментирования. Которая бы совмещала возможность
комментировать как для гостей, так и для зарегистрированных пользователей.
Хочу заметить, моей целью не было написание такой системы комментирования, которой свет не видывал. Эта система
подходит для меня и моего сайта. Если и вам она подходит, замечательно. Ну а если нет, я уверен, что из этой статьи
можно много чего подчеркнуть для себя.
Виды и варианты
Как комментирование вообще можно реализовать и какие есть подходы/варианты? Такой вот вопрос я себя задал, когда
впервые потребовалось, добавить комментирование мне на сайт.
-
Я выделил 2 основных вида систем комментирования, по тому кто может оставлять комментарии, остальные лишь их вариации и комбинации.
- Анонимное комментирование ( Любой проходимец сможет оставить у вас на сайте комментарий)
- Аутентифицированное комментирование (Комментирование только для зарегистрированных пользователей)
Анонимное комментирование
Это самое простое в реализации, но при этом самое опасное. Почему опасное ?
Тут, возможно, я не совсем корректно выразился, но под опасным я имел в виду то, что абсолютно любой человек или бот сможет оставить комментарии. В абсолютно любом количестве, и абсолютно любого качества.
Ты ведь сам понимаешь, анонимность даёт людям свободу делать и писать всё что угодно. И по своему опыту знаю,
если у человека есть возможность, поднасрать да ещё и безнаказанно, обязательно найдутся желающие.
Аутентифицированное комментирование
Комментируют только зарегистрированные пользователи.
Если вы разрабатываете социальную сеть или форум или интернет магазин, то эта форма комментирования для вас. Важно помнить, регистрация должна предоставлять некие преимущества тем кто зарегистрировался.
Опять повторюсь, если у вас блог, новостной портал или информационный портал, то не советую. Человеческая лень, это очень тяжело
преодолимое препятствие.
Что же выбрал я? И как оно работает у меня.
У меня смешанная. Как анонимусы, так и зарегистрированные пользователи могут оставлять комментарии под постами.
Представим, вы посетили мой сайт. Вы прочитали интересующую вас статью и решили оставить комментарий.
Скролите до конца, пишете то о чём думаете, жмякаете кнопку, Оставить комментарий.
Выскакивает новая форма, на кнопке, и просит вас ввести ваше имя. Вы вводите его.
Форма оставления комментария изменилась, появилось ваше имя.
Вы, жмякаете, Оставить комментарий ещё раз. Он появляется у вас на странице.
Теперь перейдём к той части, где я пошагово объясняю, как реализовать данную систему комментирования.
Создаём модель комментария
Начнём с модели Comment. Она будет хранить все наши комментарии.
-
Какие поля нам нужны?
- type ➜ поле которое указывает к какой статье оно относится
- user ➜ что за пользователь оставил комментарий( именно зарегистрированный)
- anonymous_user_name ➜ Имя, которое выбрал себе гость
- anonymous_user_id ➜ Будет необходим для возможности отличить анонимный комментарий от не анонимного
- content ➜ собственно сак коммент
- timeCreated ➜ когда был написан
Подготавливаем шаблон комментария и комментариев
-
Вообще нам потребуется 3 шаблона, минимум
- один для рендеринга одного комментария( будет использоваться, когда надо добавить новый комментарий)
- один для рендеринга группы комментариев( будет использоваться, когда надо будет подгружать их)
- и ещё один, где эти комментарии будут отображаться и подгружаться
Вот шаблон для одного комментария
{{com.content}}
{% if user.slug == com.user.slug and request.session.is_auth %}
{% endif %}
Поясню пару непонятных моментов в этом шаблоне.
Проверка, если комментарий написан анонимным пользователем
{% if not com.anonymous_user_id|length > 0%}
Проверка, установленна ли аватарка у пользователя, который опубликовал данный комментарий
{% if com.user.avatar|length > 0 %}
Теперь этот кусочек шаблона
{% if user.slug == com.user.slug and request.session.is_auth %}
{% endif %}
Это довольно важный кусочек, так сказать. Вкратце тут говорится, что только зарегистрированный пользователь
сможет удалять свои комментарии.
В шаблоне для комментариев всё абсолютно точно так же как и
для одного комментария. За тем исключением, что он нужен для рендеринга нескольких комментариев.
То есть, там используется цикл for
Подготавливаем шаблон, где эти комментарии будут появляться
Теперь рассмотрим место, где эти комментарии будут отображаться.
Вот этот код я использую в base_post.html. То есть, это шаблон от которого
у меня наследуются все остальные посты( статьи, кейсы, новости ). Короче говоря,
предоставляет базовый функционал для моих статей. Ниже в главе Готовое приложение и другие ресурсы
я оставил ссылку на файл целиком
По правде говоря я использую комментарии не только в постах. У каждого зарегистрированного
пользователя есть свой кабинет, где он может просмотреть все оставленные им комментарии. И удалить,
если захочет.
Расмотрим ту часть base_post.html которая отвечает за комментарии.
Сначала идёт сама форма оставления комментария
...
{% csrf_token %}
Дальше есть кнопка оставить комментарий. Для аутентифицированных пользователей, она просто оставит комментарий.
Но для анонимов, будет отображена другая кнопка ( кнопка всё таже, просто функционал у неё другой)
Она запросит только имя анонима. После чего, будет заменена на обычную кнопку отправления комментария.
Для анонимов
{% if not request.session.is_auth %}
Для зарегистрированных
{% else %}
{% endif %}
Пишем ajax запрос для оставления комментария
Шаблоны это конечно хорошо, но теперь надо связать серверную часть с фронтендом. Напишем ajax скрипт
Не забудем подключить его в base_post.html
{% block scripts %}
{% csrf_token %}
{% block scripts_post %}
{% endblock %}
{% endblock %}
Небольшое пояснение. Сначала мы подключаем jQuery библиотеку.
Потом сохраняем csrf токен и то какой пост пользователь просматривает сейчас
{% csrf_token %}
Дальше подключаем наш скрипт для работы комментариев( там будут находиться наши ajax запросы )
И так как от этого шаблона наследуются шаблоны статей, новостей и кейсов, им могут быть
необходимы отдельные скрипты ( или стили ).
Поэтому это здесь.
{% block scripts_post %}
{% endblock %}
Когда нажимается кнопка Отправить коментарий(будь то, от авторизованного или анонимного пользователя)
Выполняется данная функция.
function sendComment(path){
var post = post_slug
var about = $("#about").val()
var username = $("#comments_el__user_name_commentAdd").text()
$.ajax({
type: "POST",
url: "/" + language_code + "/" + path + "/",
data: {
'post': post,
'about': about,
'username': username
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
$(result).insertAfter("#comment_add")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
},
})
}
Где мы, отправляем на сервер имя текущего поста, сообщение, и имя пользователя
var post = post_slug
var about = $("#about").val()
var username = $("#comments_el__user_name_commentAdd").text()
При успехе, мы рендерим comment.html и вставляем полученый результат над всеми комментариями.
И ещё не забываем добавить событие для возможности удаления данного комментария.
$(result).insertAfter("#comment_add")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
Пишем ajax запрос для загрузки комментариев
Загружать комментарии на сервер это отлично, но и другие пользователи должны видеть
комментарии. Вот функция для отображения комментариев для одного поста.
function loadComments(){
var post = post_slug
$.ajax({
type: "POST",
url: "/" + language_code + "/load_comments/",
data: {
'post': post,
'number': number,
'offset': offset,
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
$(result).insertBefore("#scroll-sentinel")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
offset = offset + number
},
})
}
Важно отметить, эта функция срабатывает, когда полоса прокрутки дошла до конца.
Для этого используется специальный наблюдатель, который и следит за этим.
$(document).ready(function(){
default_avatar_path = $("#comments_el__user_avatar_commentAdd").attr('src')
const observer = new IntersectionObserver((entries, observer) => {
// Loop through the entries
for (const entry of entries) {
// Check if the entry is intersecting the viewport
if (entry.isIntersecting) {
// Load more content
loadComments()
}
}
});
const scrollSentinel = document.querySelector("#scroll-sentinel");
observer.observe(scrollSentinel);
...
Пишем ajax запрос для удаления комментария
И небольшой бонус. Как удалять комментарии.
Из предыдущих разделов вы увидели, как и где назначаются события для удаления комментария.
Теперь посмотрим на то как он удаляется.
function removeComment(toRemove){
var post = post_slug
var comment_id = $(toRemove).attr("commentid")
$.ajax({
type: "POST",
url: "/" + language_code + "/remove_comment/",
data: {
'post': post,
'comment_id': comment_id,
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
// Removes only client side part
$(toRemove).parent().remove()
},
})
}
Как ты заметил, чтобы удалить комментарий нужен его id. Это кастомный атрибут, который мы
заполняем когда рендерим комментарий или комментарии.
Вот он
{% if user.slug == com.user.slug and request.session.is_auth %}
Настраиваем представления и адреса
Почти всё готово. Осталось только разобраться с маршрутами и самими представлениями.
Comment/urls.py выглядит так:
from django.urls import path
from .views import *
urlpatterns = [
path('send_comment_guesting/', send_comment_guesting),
path('send_comment_authorized/', send_comment_authorized),
path('remove_comment/', remove_comment),
path('prepare_user/', prepare_user),
path('load_comments/', load_comments),
path('load_comments_by_user/', load_comments_by_user),
]
Разберём каждое представление по отдельности. Кроме, конечно, load_comments_by_user.
Эту функцию ты сможешь посмотреть и сам если захочешь. Мы разбираем комментирование
на примере постов.
def send_comment_guesting(request):
media_root = settings.MEDIA_URL
if request.method == 'POST':
type = Post.objects.filter(slug=request.POST['post']).get()
content = request.POST['about']
username = request.POST['username']
user_id = request.session.session_key
comment = Comment(
anonymous_user_id=user_id,
anonymous_user_name=username,
type=type,
content=content
)
comment.save()
context = {
'com': comment,
'media_root': media_root,
}
return render(request, "Comment/comment.html", context=context)
Сохраняем комментарий как анонимный. Где:
- type ➜ пост к которому принадлежит комментарий
- content ➜ содержимое комментария
- username ➜ имя анонимного пользователя
- user_id ➜ текущий id сессии
Дальше, создаётся запись в базе данных и рендерится страница с коментарием.
def send_comment_authorized(request):
media_root = settings.MEDIA_URL
if request.method == 'POST':
user = User.objects.filter(name=request.session.get('username')).get()
type = Post.objects.filter(slug=request.POST['post']).get()
content = request.POST['about']
comment = Comment(user=user, type=type, content=content)
comment.save()
context = {
'com': comment,
'user': user,
'media_root': media_root,
}
return render(request, "Comment/comment.html", context=context)
Сохраняем комментарий как авторизованный. Почти всё тоже самое, только здесь
добавляется объект пользователя, который добавил комментарий.
def prepare_user(request):
data = {
'isValid': False,
'username': None,
}
if request.method == "GET":
username = request.GET['username']
data['isValid'] = True
data['username'] = username
return JsonResponse(data)
Можно считать данную функцию служебной ведь, единственная задача данной функции это
вставить имя пользователя в форму для создания комментария.
def remove_comment(request):
if request.method == 'POST':
comment_id = request.POST['comment_id']
comment = Comment.objects.filter(id=comment_id).get()
comment.delete()
status = 200
return JsonResponse({}, status=status)
Удаляем комментарий по id.
def load_comments(request):
try:
user = User.objects.filter(name=request.session.get('username')).get()
except:
user = None
media_root = settings.MEDIA_URL
if request.method == 'POST':
number = request.POST.get('number', 5)
offset = request.POST.get('offset', 0)
type = Post.objects.filter(slug=request.POST['post']).get()
comments = Comment.objects.filter(type=type).order_by('-timeCreated')[(int(offset)):(int(number)) + (int(offset))]
context = {
'comments': comments,
'user': user,
'media_root': media_root,
}
return render(request, "Comment/comments.html", context=context)
Загружаем комментарии.
Сначала проверяем, авторизированый ли пользователь просит загрузку.
try:
user = User.objects.filter(name=request.session.get('username')).get()
except:
user = None
Дальше, мы определяем сколько, с какого комментария и с какой статьи загрузить комментарии.
number = request.POST.get('number', 5)
offset = request.POST.get('offset', 0)
type = Post.objects.filter(slug=request.POST['post']).get()
comments = Comment.objects.filter(type=type).order_by('-timeCreated')[(int(offset)):(int(number)) + (int(offset))]
После рендерим найденные комментарии и возвращаем пользователю. Он возвращается здесь:
function loadComments():
...
success: function(result){
$(result).insertBefore("#scroll-sentinel")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
offset = offset + number
},
Где мы вставляем полученные комментарии на страницу, увеличиваем счётчик и назначаем событие для удаления
комментария.
Готовое приложение и другие ресурсы
Пост Скриптум. Вывод
Итак, теперь когда статья закончилась я бы хотел извиниться за то что статья не вышла такой, какой она должна была
выйти. Сейчас поясню.
Изначально я планировал рассказать про 3 возможных варианта написания комментариев.
Такие как:
- Анонимное
- Через социальные сети
- Только для зарегистрированных
Но увы, как только я понял, как много это времени у меня займёт, я решил немного поостудить свой пыл. И написать статью
именно о моём пути написания системы комментирования. То есть, комбинированный вариант анонимного/зарегистрированного
комментирования.
Ещё я хотел вставить формы оставления комментариев по каждому виду. То есть, начать с самого простого, потом перейти к
комментированию через социальные сети, и закончить всё тем что комментировать смогут лишь зарегистрированые пользователи.
Которых кстати у меня ещё нет.
Может, когда-нибудь, я напишу данную статью в том виде что хотел, ну а пока имеем, что имеем.
Комментарии
(0)
Отправить
Сейчас тут пусто. Буть первым (o゚v゚)ノ
Использованные термины
- Django шаблон ⟶ Это текстовый документ, который размечен специальным ситнаксисом для вставки кода в него.
- Джанго представления ⟶ Это функции или классы, которые обрабатывают HTTP-запросы и возвращают HTTP-ответы. Они отвечают за бизнес-логику вашего приложения и связаны с моделями данных, чтобы взять информацию из базы данных и отобразить её пользователю.
- Джанго фреймворк ⟶ Это высокоуровневый веб-фреймворк на языке программирования Python, который позволяет разработчикам создавать веб-приложения быстрее и с меньшими затратами на время благодаря своим мощным инструментам и встроенным функциям. Он был разработан для упрощения разработки сложных веб-сайтов и предоставляет множество «из коробки» функций
- Вебсайт ⟶ Это совокупность связанных между собой веб-страниц, доступных через интернет и имеющих одно общее доменное имя. Каждый веб-сайт может содержать текстовую информацию, изображения, видео и другие мультимедийные элементы. Веб-сайты могут выполнять различные функции, включая предоставление информации, общение, онлайн-торговлю и множество других взаимодействий.
Релевантные вопросы
- Я не переношу язык шаблонов Django. Мне обязательно его использовать? Я думаю, что этот шаблонизатор — лучшее, что когда-либо было, но я знаю, что выбор языка шаблонов — это почти религия. В Django нет ничего, что требовало бы использования языка шаблонов, так что если вы привязаны к Jinja2, Mako или чему-то еще, то это нормально.
- Как можно увидеть необработанные SQL-запросы, выполняемые Django? Убедитесь, что настройка Django DEBUG установлена на True. Затем импортируйте соединение из django.db. connection.queries доступен только если DEBUG имеет значение True. Это список словарей в порядке выполнения запроса. Каждый словарь имеет свойство sql и time.
- Если я внесу изменения в модель, как мне обновить базу данных? Взгляните на поддержку Django для миграции схем. Если вы не против очистки данных, утилита manage.py вашего проекта имеет опцию сброса, чтобы сбросить базу данных в состояние, в котором она была сразу после выполнения миграции.