Добавление copy_relations за пределами плагина с помощью signals в django cms
6 июля 2021 г. 8:59
В django-cms иногда необходимо добавить или расширить copy_relations() в модели стороннего плагина для обработки внешних ключей: ForeignKey, OneToOneField или ManyToManyField.
Например, есть некое стороннее приложение (cms плагин) text_block с моделью TextBlock:
# models.py
class TextBlock(CMSPlugin):
title = models.CharField(_('Title'), max_length=255)
content = models.TextField(_('Content'))
# cms_plugins.py
class TextBlockPlugin(CMSPluginBase):
model = TextBlock
name = 'TextBlock'
render_template = 'text_block.html'
И нам нужно добавить какое-нибудь дополнительное поле, например, description (описание).
Для этого добавляем в проект новое приложение, например, text_block_ext с моделью TextBlockExt:
class TextBlockExt(models.Model):
text_block = models.OneToOneField(TextBlock, on_delete=models.CASCADE)
description = models.TextField(_('Description'))
Ext (в слове TextBlockExt) - это сокращение слова Extension (расширение) - обычно я так называю модели, которые расширяют чью-либо функциональность.
Теперь надо добавить поле description в форму редактирования TextBlock. Это можно сделать с помощью добавления inline объекта admin-класса для модели TextBlock и перерегистрации админ-класса в файле cms_plugins.py:
from text_block.cms_plugins import TextBlockPlugin as BaseTextBlockPlugin
from text_block_ext.models import TextBlockExt
plugin_pool.unregister_plugin(BaseTextBlockPlugin)
class TextBlockExtInline(admin.StackedInline):
model = TextBlockExt
extra = 1
can_delete = False
verbose_name = _('Additional options')
verbose_name_plural = _('Additional options')
@plugin_pool.register_plugin
class TextBlockPlugin(BaseTextBlockPlugin):
render_template = 'text_block_ext.html'
inlines = (TextBlockExtInline, )
При добавлении / редактировании плагина TextBlock теперь мы увидим наш инлайн-объект с полем description. Введя какое-нибудь значение и сохранив объект, мы увидим, что наш шаблон выдаёт сохранённые данные, например, в таком шаблоне:
<div class="text_block">
<h2>{{ instance.title }}</h2>
{% instance.textblockext.description %}<div>Description = {{ instance.textblockext.description }}</div>{% endif %}
<div>{{ instance.content }}</div>
</div>
Но проблема в том, что при публикации страницы мы не увидим сохранённого поля description, так как django-cms при публикации не обрабатывает внешние ключи. Обычным способом это делается через добавление copy_relations() в модель плагина, но так как у нас сторонний плагин, то нужно как-то обработать внешние ключи.
Добавление сигнала post_publish в наше приложение
К счастью, django-cms предоставляет сигнал post_publish, в котором мы можем добраться до нашего опубликованного компонента и добавить внешний ключ TextBlockExt. Добавляем получатель сигнала в text_block_ext/signals.py, где обработаем внешние ключи:
from django.dispatch import receiver
from cms.signals import post_publish
from text_block.models import TextBlock
@receiver(post_publish)
def copy_relations(sender, instance, language, **kwargs):
published_text_blocks = TextBlock.objects.filter(placeholder__page=instance.publisher_public)
for published_text_block in published_text_blocks:
text_block_parent = TextBlock.objects.get(id=published_text_block.parent_instance_id)
if hasattr(text_block_parent, 'textblockext'):
text_block_ext = text_block_parent.textblockext
text_block_ext.id = None
published_text_block.textblockext = text_block_ext
published_text_block.textblockext.save()
У опубликованного плагина есть свойство parent_instance_id, через который можно получить неопубликованный плагин text_block_parent: TextBlock.objects.get(id=published_text_block.parent_instance_id). Чтобы скопировать text_block_ext, нужно просто к id присвоить None. И далее уже к published_text_block.textblockext присваиваем скопированный text_block_ext.
Теперь после публикации страницы опубликованные плагины получат объект text_block_ext.
Возможности сигнала post_publish не ограничивается добавлением кастомных copy_relations() для сторонних моделей. В сигнале можно много разных полезных для сайта функций выполнять, например, генерировать свежую версию sitemap.xml, производить оповещение главному редактору сайта о том, что была произведена публикация сайта или обновлять статистику по опубликованным страницам и т. д.
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0