如何访问Django CMS插件的父模型

如何访问Django CMS插件的父模型,django,django-models,django-cms,Django,Django Models,Django Cms,我创建了2个django cms插件,一个父“容器”,可以包含多个子“内容”插件 当我保存子插件时,我想访问父插件的模型 from cms.plugin_pool import plugin_pool from cms.plugin_base import CMSPluginBase from .models import Container, Content class ContainerPlugin(CMSPluginBase): model = Container na

我创建了2个django cms插件,一个父“容器”,可以包含多个子“内容”插件

当我保存子插件时,我想访问父插件的模型

from cms.plugin_pool import plugin_pool
from cms.plugin_base import CMSPluginBase

from .models import Container, Content

class ContainerPlugin(CMSPluginBase):
    model = Container
    name = "Foo Container"
    render_template = "my_package/container.html"
    allow_children = True
    child_classes = ["ContentPlugin"]

class ContentPlugin(CMSPluginBase):
    model = content
    name = "Bar Content"
    render_template = "my_package/content.html"
    require_parent = True
    parent_classes = ["ContainerPlugin"]
    allow_children = True

    def save_model(self, request, obj, form, change):
        response = super(ContentPlugin, self).save_model(
            request, obj, form, change
        )

        # here I want to access the parent's (container) model, but how?

        return response

plugin_pool.register_plugin(ContainerPlugin)
plugin_pool.register_plugin(ContentPlugin)
obj
是当前的插件实例,因此我可以获得该模型的所有属性,但我不知道如何访问父插件模型。有
obj.parent
,但据我所知,它不是插件实例。还尝试过使用
self.cms\u plugin\u实例
obj.parent.get\u plugin\u实例()
,但没有成功


有什么建议吗?

给定一个插件实例,
实例。get\u plugin\u instance()
方法返回一个元组,其中包含:

  • 实例-插件实例
  • plugin-关联的插件类实例 获取插件实例
  • 这样可以得到父对象:

    instance, plugin_class = object.parent.get_plugin_instance()
    
    选择1 查看
    CMSPluginBase
    的源代码,您可能能够使用
    get\u child\u类的实现。不幸的是,该方法实际上只返回类名,因此不能直接使用它。但我认为它实际上会迭代子实例以获得类名:

    def get_child_classes(self, slot, page):
        from cms.utils.placeholder import get_placeholder_conf
    
        template = page and page.get_template() or None
    
        # config overrides..
        ph_conf = get_placeholder_conf('child_classes', slot, template, default={})
        child_classes = ph_conf.get(self.__class__.__name__, self.child_classes)
        if child_classes:
            return child_classes
        from cms.plugin_pool import plugin_pool
        installed_plugins = plugin_pool.get_all_plugins(slot, page)
        return [cls.__name__ for cls in installed_plugins]
    
    您感兴趣的是以下两行:

        from cms.plugin_pool import plugin_pool
        installed_plugins = plugin_pool.get_all_plugins(slot, page)
    

    选择2 另一种方法(我在代码中使用的方法)是使用信号,尽管这也需要找到正确的对象。代码的可读性不是很好(请参阅我的内联注释),但它可以工作。它是前一段时间写的,但我仍然在django cms 3.2.3中使用它

    占位符名称实际上是您为占位符配置的名称。当然,最好将其移动到设置或其他位置。不过,我不知道为什么我没有这么做

    我对你的解决方案很感兴趣

    # signals.py
    import itertools
    import logging
    
    from cms.models import CMSPlugin
    from cms.plugin_pool import plugin_pool
    from django.db import ProgrammingError
    from django.db.models.signals import post_save
    
    logger = logging.getLogger(__name__)
    _registered_plugins = [CMSPlugin.__name__]
    
    
    def on_placeholder_saved(sender, instance, created, raw, using, update_fields, **kwargs):
        """
        :param sender: Placeholder
        :param instance: instance of Placeholder
        """
        logger.debug("Placeholder SAVED: %s by sender %s", instance, sender)
        # TODO this is totally ugly - is there no generic way to find out the related names?
        placeholder_names = [
            'topicintro_abstracts',
            'topicintro_contents',
            'topicintro_links',
            'glossaryentry_explanations',
        ]
        fetch_phs = lambda ph_name: _fetch_qs_as_list(instance, ph_name)
        container = list(itertools.chain.from_iterable(map(fetch_phs, placeholder_names)))
        logger.debug("Modified Placeholder Containers %s (%s)", container, placeholder_names)
        if container:
            if len(container) > 1:
                raise ProgrammingError("Several Containers use the same placeholder.")
            else:
                # TODO change modified_by (if possible?)
                container[0].save()
    
    def _fetch_qs_as_list(instance, field):
        """
        :param instance: a model
        :param field: optional field (might not exist on model)
        :return: the field values as list (not as RelatedManager)
        """
        qs = getattr(instance, field)
        fields = qs.all() if qs else []
        return fields
    
    def on_cmsplugin_saved(sender, instance, created, raw, using, update_fields, **kwargs):
        """
        :param sender: CMSPlugin or subclass
        :param instance: instance of CMSPlugin
        """
        plugin_class = instance.get_plugin_class()
        logger.debug("CMSPlugin SAVED: %s; plugin class: %s", instance, plugin_class)
        if not plugin_class.name in _registered_plugins:
            post_save.connect(on_cmsplugin_saved, sender=plugin_class)
            _registered_plugins.append(plugin_class.name)
            logger.info("Registered post_save listener with %s", plugin_class.name)
        on_placeholder_saved(sender, instance.placeholder, created, raw, using, update_fields)
    
    def connect_existing_plugins():
        plugin_types = CMSPlugin.objects.order_by('plugin_type').values_list('plugin_type').distinct()
        for plugin_type in plugin_types:
            plugin_type = plugin_type[0]
            if not plugin_type in _registered_plugins:
                plugin_class = plugin_pool.get_plugin(plugin_type)
                post_save.connect(on_cmsplugin_saved, sender=plugin_class)
                post_save.connect(on_cmsplugin_saved, sender=plugin_class.model)
                _registered_plugins.append(plugin_type)
                _registered_plugins.append(plugin_class.model.__name__)
        logger.debug("INIT registered plugins: %s", _registered_plugins)
    
    post_save.connect(on_cmsplugin_saved, sender=CMSPlugin)
    
    你必须在某处设置这些信号。我在我的urls.py中这样做,尽管应用程序配置可能是它的合适位置?(我试图避免应用程序配置。)


    因为子插件总是继承上下文。在父模板中,您可以执行以下操作:

    {% with something=instance.some_parent_field %}
      {% for plugin in instance.child_plugin_instances %}
              {% render_plugin plugin %}
      {% endfor %}
    {% endwith %}
    

    并在您的孩子模板中使用一些内容。

    我感谢您的努力+1为此。。。几小时前我下载了PyCharm,为了实际运行调试器,瞧,我可以使用
    obj.parent.container.[…property…]
    访问它。因为我确信父对象是我的“ContainerPlugin”,所以我可以使用这个硬编码的名称。不确定django如何生成它(从“ContainerPlugin”到“container”)。在使用PyCharm进行调试后,我发现我实际上可以使用
    obj.parent.container
    ,但您的解决方案是正确的。谢谢!如果您在模板中需要它,可以使用
    {{instance.parent.get\u plugin\u instance.0}
    。不喜欢插件中的列表索引,但是为它写一个标签似乎是…这是一个伟大而干净的解决方案!谢谢,它真的帮助了我。
    {% with something=instance.some_parent_field %}
      {% for plugin in instance.child_plugin_instances %}
              {% render_plugin plugin %}
      {% endfor %}
    {% endwith %}