• django-cms 代码研究(六)plugin的深入分析


    示例代码:

    https://github.com/divio/djangocms-picture

    以上一个图片的插件,安装后可在页面中添加图片,效果如下图:

    以此为切入点,分析plugin的逻辑:

    分析

    1. plugin列表加载分析:

    2. plugin实例添加到页面的分析:

    通过DjDT分析得出,最终是交给了cms.admin.pageadmin.edit_plugin处理,如下:

    而edit_plugin的代码非常简洁:

        def edit_plugin(self, *args, **kwargs):
            with create_revision():
                return super(PageAdmin, self).edit_plugin(*args, **kwargs)
    

    而PageAdmin的继承关系如下:

    class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
    

    基本可以猜出,edit_plugin应该是mixin里面的函数,如下:

     

    具体代码:

        @xframe_options_sameorigin
        def edit_plugin(self, request, plugin_id):
            plugin_id = int(plugin_id)
            cms_plugin = get_object_or_404(CMSPlugin.objects.select_related('placeholder'), pk=plugin_id)
    
            instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
            if not self.has_change_plugin_permission(request, cms_plugin):
                return HttpResponseForbidden(force_unicode(_("You do not have permission to edit this plugin")))
            plugin_admin.cms_plugin_instance = cms_plugin
            try:
                plugin_admin.placeholder = cms_plugin.placeholder
            except Placeholder.DoesNotExist:
                pass
            if request.method == "POST":
                # set the continue flag, otherwise will plugin_admin make redirect to list
                # view, which actually doesn't exists
                request.POST['_continue'] = True
            if request.POST.get("_cancel", False):
                # cancel button was clicked
                context = {
                    'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
                    'plugin': cms_plugin,
                    'is_popup': True,
                    "type": cms_plugin.get_plugin_name(),
                    'plugin_id': plugin_id,
                    'icon': force_escape(escapejs(cms_plugin.get_instance_icon_src())),
                    'alt': force_escape(escapejs(cms_plugin.get_instance_icon_alt())),
                    'cancel': True,
                }
                instance = cms_plugin.get_plugin_instance()[0]
                if instance:
                    context['name'] = force_unicode(instance)
                else:
                    # cancelled before any content was added to plugin
                    cms_plugin.delete()
                    context.update({
                        "deleted": True,
                        'name': force_unicode(cms_plugin),
                    })
                return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
    
            if not instance:
                # instance doesn't exist, call add view
                response = plugin_admin.add_view(request)
            else:
                # already saved before, call change view
                # we actually have the instance here, but since i won't override
                # change_view method, is better if it will be loaded again, so
                # just pass id to plugin_admin
                response = plugin_admin.change_view(request, str(plugin_id))
            if request.method == "POST" and plugin_admin.object_successfully_changed:
                self.post_edit_plugin(request, plugin_admin.saved_object)
                saved_object = plugin_admin.saved_object
                context = {
                    'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
                    'plugin': saved_object,
                    'is_popup': True,
                    'name': force_unicode(saved_object),
                    "type": saved_object.get_plugin_name(),
                    'plugin_id': plugin_id,
                    'icon': force_escape(saved_object.get_instance_icon_src()),
                    'alt': force_escape(saved_object.get_instance_icon_alt()),
                }
                return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
            return response
    

    经过调试发现,在

    cms_plugin = get_object_or_404(CMSPlugin.objects.select_related('placeholder'), pk=plugin_id)

    时plugin_id已经有值了,因此猜测,此id为plugin控件在控件表CMSPlugin中的id(CMSPlugin通过一个名为placeholder的字段m2m到Placeholder表,如下:)

    @python_2_unicode_compatible
    class CMSPlugin(with_metaclass(PluginModelBase, MPTTModel)):
       ...
       placeholder = models.ForeignKey(Placeholder, editable=False, null=True)

    placehoder似乎和plugin等价了,不过看不出玄机:

    @python_2_unicode_compatible
    class Placeholder(models.Model):
        slot = models.CharField(_("slot"), max_length=50, db_index=True, editable=False)
        default_width = models.PositiveSmallIntegerField(_("width"), null=True, editable=False)
        cache_placeholder = True
    
    ...
    
        def get_plugins_list(self, language=None):
            return list(self.get_plugins(language))
    
        def get_plugins(self, language=None):
            if language:
                return self.cmsplugin_set.filter(language=language).order_by('tree_id', 'lft')
            else:
                return self.cmsplugin_set.all().order_by('tree_id', 'lft')
    

    回到正题:

    instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
    

    新建时,instance为None,plugin_admin为PicturePlugin的实例。分析源码,得出,plugin是从控件池中取出来的,如下:

        def get_plugin_class(self):
            from cms.plugin_pool import plugin_pool
    
            return plugin_pool.get_plugin(self.plugin_type)
    
        def get_plugin_class_instance(self, admin=None):
            plugin_class = self.get_plugin_class()
            # needed so we have the same signature as the original ModelAdmin
            return plugin_class(plugin_class.model, admin)
    
        def get_plugin_instance(self, admin=None):
            plugin = self.get_plugin_class_instance(admin)
            if hasattr(self, "_inst"):
                return self._inst, plugin
            if plugin.model != self.__class__: # and self.__class__ == CMSPlugin:
                # (if self is actually a subclass, getattr below would break)
                try:
                    instance = plugin.model.objects.get(cmsplugin_ptr=self)
                    instance._render_meta = self._render_meta
                except (AttributeError, ObjectDoesNotExist):
                    instance = None
            else:
                instance = self
            self._inst = instance
            return self._inst, plugin
    

    最终,由于instance为空,进入以下逻辑:

            if not instance:
                # instance doesn't exist, call add view
                response = plugin_admin.add_view(request)
    

    此处分析也有些力不从心(后续深入)

    =============

    经过以上分析,plugin列表的加载逻辑已经呼之欲出了,留给细心的读者自己发掘。

    总结:

    1. 控件加载用到的类:

       CMSPlugin/PlaceholderAdminMixin/Placeholder/PageAdmin/Page等,继承关系如下:

    2. 当在页面中添加plugin时,调用关系:

      1). PageAdmin.edit_plugin

      2). PlaceholderAdminMixin.edit_plugin , 其处理逻辑是:

          a). 首先通过CMSPlugin得到一个cms_plugin对象。

          b). 通过cms_plugin对象得到控件实例instance和plugin_admin,其中plugin_admin是重插件池 plugin_pool中根据类型得到的。

          c). 如果实例为空(新建),通过plugin_admin的add_view方法返回一个response对象,否则(编辑),通过change_view方法返回response对象。

          d). 如果请求方式是Post,并且plugin_admin的object_successfully_changed为真值,则给予相关提示信息。

      

  • 相关阅读:
    知识工程及语义网技术 2020-03-19 (第一节)-构建本体
    知识工程及语义网技术 2020-03-12 (第二节)-构建本体
    知识工程及语义网技术 2020-03-12 (第二节)、RDF(S)、OWL
    知识工程及语义网技术(一)-XML、RDF(S)、OWL-2020.3.5第一节
    知识工程及语义网技术(一)-知识工程,万维网、语义网、本体工程 2020-02-20 (第一节)
    本体
    语义网技术及其应用(四)-欧石燕
    一文深度揭秘3GPP:2G/3G/4G/Pre-5G标准化制定流程是这样的
    3GPP更新5G标准时间表
    一张图了解3GPP
  • 原文地址:https://www.cnblogs.com/Tommy-Yu/p/3963098.html
Copyright © 2020-2023  润新知