Добавление copy_relations за пределами плагина с помощью signals в django cms

6 июля 2021 г. 4:59

В django-cms иногда необходимо добавить или расширить copy_relations() в модели стороннего плагина для обработки внешних ключей: ForeignKeyOneToOneField или 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_ext = TextBlock.objects.get(id=published_text_block.parent_instance_id).textblockext
        text_block_ext.id = None

        published_text_block.textblockext = text_block_ext
        published_text_block.textblockext.save()

У опубликованного плагина есть свойство parent_instance_id, через который можно получить неопубликованный плагин text_blockTextBlock.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 из 5 (всего 0 оценок)

captcha
Отмеченные звёздочкой поля ( * ) являются обязательными для заполнения.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор статьи

Права на использование материала, расположенного на этой странице https://vivazzi.pro/it/copy-relations-signals/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="https://vivazzi.pro/it/copy-relations-signals/">https://vivazzi.pro/it/copy-relations-signals/</a>

Подробнее: Правила использования сайта

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь.

Но зарегистрировавшись, вы сможете:

  • получать оповещения об ответах
  • просматривать свои комментарии
  • иметь возможность использовать все функции разработанных сервисов

Для комментрирования от своего имени, войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте

Выберите валюту для отображения денежных единиц