Урок 17 / 22 · Commentail 1.3.2
Этот урок для проекта, где нужна дискуссия у объекта, который не является Wagtail Page: например, товара. Сначала пройдите обычное подключение из урока 2. Ниже пример отдельного приложения shop; если у вас уже есть Product, адаптируйте существующую модель, не создавайте вторую.
Модель и страница товара
В новом учебном проекте создайте приложение python manage.py startapp shop, добавьте shop в INSTALLED_APPS. В shop/models.py:
from django.db import models
from django.urls import reverse
class Product(models.Model):
site = models.ForeignKey("wagtailcore.Site", on_delete=models.CASCADE, related_name="shop_products")
title = models.CharField(max_length=200)
is_public = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("product-detail", args=[self.pk])
В shop/views.py:
from django.http import Http404
from django.shortcuts import get_object_or_404, render
from wagtail.models import Site
from shop.models import Product
def product_detail(request, pk):
site = Site.find_for_request(request)
if site is None:
raise Http404
product = get_object_or_404(Product, pk=pk, site=site)
if not product.is_public and not request.user.is_staff:
raise Http404
return render(request, "shop/product_detail.html", {"product": product})
Site в запросе и Site товара должны совпадать. Проверка страницы товара нужна отдельно от проверки Commentail. В корневом urls.py импортируйте path, если его ещё нет, и добавьте до Wagtail catch-all:
from shop.views import product_detail
# Insert before Wagtail's catch-all in the existing urlpatterns.
path("products/<int:pk>/", product_detail, name="product-detail"),
Адаптер и доступ к почте
В shop/comments.py:
from shop.models import Product
class ProductAdapter:
def get_site(self, obj, request):
return obj.site
def can_view(self, obj, request):
return obj.is_public or request.user.is_staff
def get_url(self, obj, site):
return obj.get_absolute_url()
def can_send_product_email(recipient, target, site):
"""Allow guest mail only for this Site's public products."""
return (
isinstance(target, Product)
and target.site_id == site.pk
and target.is_public
)
В default существующего COMMENTAIL:
COMMENTAIL = {
"default": {
"OBJECT_ADAPTERS": {"shop.Product": "shop.comments.ProductAdapter"},
"CAN_SEND_GUEST_EMAIL": "shop.comments.can_send_product_email",
},
}
Этот callback разрешает гостевые письма только для публичных товаров нужного Site. Если в проекте несколько типов объектов или закрытые Page, расширьте единую функцию с отдельными проверками, а не заменяйте правило безусловным True. Для обычных аккаунтов обработчик проверяет адаптер с учётной записью получателя и пустой сессией; браузерные подтверждения в нём недоступны.
Чтобы пример работал и на учебном сайте без существующего base.html, создайте templates/course_base.html:
{% load i18n commentail_tags %}
{% get_current_language as current_language %}
<!doctype html>
<html lang="{{ current_language }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ product.title }}</title>
{% commentail_css %}
{% commentail_js %}
</head>
<body>
<main>{% block content %}{% endblock %}</main>
</body>
</html>
В templates/shop/product_detail.html:
{% extends "course_base.html" %}
{% load commentail_tags %}
{% block content %}
<h1>{{ product.title }}</h1>
{% commentail product %}
{% endblock %}
На своём сайте вместо course_base можно наследовать существующий базовый шаблон с ресурсами Commentail и его блоком content. Создайте таблицы python manage.py makemigrations shop, затем python manage.py migrate и python manage.py check.
Создадим учебный товар
Откройте python manage.py shell. Вставьте блок ниже, заменив YOUR_TEST_HOST и порт точными значениями из настроек Wagtail Site. Для неизменённого учебного примера это 127.0.0.1 и 8000.
from wagtail.models import Site
from shop.models import Product
site = Site.objects.get(hostname="YOUR_TEST_HOST", port=8000)
product = Product.objects.create(site=site, title="Practice product", is_public=True)
print(product.get_absolute_url())
Откройте напечатанный адрес на том же сайте. DoesNotExist при поиске Site означает, что hostname/port не совпали; исправьте их, не создавайте случайный новый Site ради устранения ошибки.
После первого открытия появится запись в Comments → Object discussions; там задаются исключения настроек объекта. Проверьте публичный товар гостем, закрытый товар гостем и редактором, другой Site и полный почтовый путь. Поддерживаются конкретные непрокси-модели с целым, UUID или строковым ключом до 255 символов.
Удаление объекта через обычный Django ORM удаляет связанные обсуждения и служебные данные. Сырой SQL и исторические модели data migration этот runtime-механизм обходят: их очистка требует отдельного кода. До удаления рабочего товара проверьте требуемую политику сохранения данных.
Другие уроки по этой задаче
- 2. Подключаем Commentail к существующему Wagtail
- 15. Аватары, имена и динамические шаблоны
- 19. Рабочий сайт: nginx, кеш и данные пользователей
Выберите способ входа или оставьте комментарий как гость ниже.