示例代码:

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为真值,则给予相关提示信息。

  

django-cms 代码研究(六)plugin的深入分析的更多相关文章

  1. dedecms代码研究六

    今天讲的是dedecms最关键的东西,模板分析啦.也就是dedetag.class.php 里面的ParseTemplet方法 模板解析方法 先看看一个dedecms标签,大家心里有个数: {dede ...

  2. Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)

    编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...

  3. Ningx代码研究.

    概述 研究计划 参与人员 研究文档 学习emiller的文章 熟悉nginx的基本数据结构 nginx 代码的目录结构 nginx简单的数据类型的表示 nginx字符串的数据类型的表示 内存分配相关 ...

  4. CWMP开源代码研究——git代码工程

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  5. dedecms代码研究二

    dedecms代码研究(2)从index开始现在继续,今天讲的主要是dedecms的入口代码.先打开index.PHP看看里面是什么吧.打开根目录下的index.php嗯映入眼帘的是一个if语句.检查 ...

  6. DEDECMS数据库执行原理、CMS代码层SQL注入防御思路

    我们在上一篇文章中学习了DEDECMS的模板标签.模板解析原理,以及通过对模板核心类的Hook Patch来对模板的解析流量的攻击模式检测,达到修复模板类代码执行漏洞的目的 http://www.cn ...

  7. 手机自动化测试:Appium源码分析之跟踪代码分析六

    手机自动化测试:Appium源码分析之跟踪代码分析六   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...

  8. Django之代码风格

    1 代码风格 稍微关注一下下面这些代码标准风格指导规则将会对你大有益处,我们高度建议你通读词章,即便你此时可能正想跳过它. 1.1 让你的代码保持可读性的重要性 代码在读方面的重要性胜过写.一个代码块 ...

  9. 一段markdown编辑器代码研究

    一段markdown编辑器代码研究 说明 代码在 https://github.com/dukeofharen/markdown-editor 之所以选择这个来分析是一方面是因为它的代码结构比较简单, ...

  10. [转载]iOS6新特征:UICollectionView官方使用示例代码研究

    原文地址:iOS6新特征:UICollectionView官方使用示例代码研究作者:浪友dans 注:这里是iOS6新特征汇总贴链接 iOS6新特征:参考资料和示例汇总 这个链接可以学习到UIColl ...

随机推荐

  1. Bootstrap3.0学习第十八轮(JavaScript插件——下拉菜单)

    详情请查看 http://aehyok.com/Blog/Detail/25.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:h ...

  2. Daily Scrum – 1/5

    Meeting Minutes 开始了新的sprint: 开始准备英语版本的翻译: Progress part 组员 今日工作 Time (h) 明日计划 Time (h)   Wei         ...

  3. XMLHttpRequest的POST中文表单问题解决方案

    XMLHttpRequest的POST中文表单问题解决方案 由于XMLHttpRequest POST的内容是用UTF-8编码,所以在服务端要先把request的编码改为UTF-8. 而且客户端pos ...

  4. 【POJ 2104】 K-th Number 主席树模板题

    达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...

  5. Java中的继承、封装、多态、抽象

    1.继承 java 和某些面向对象语言(如 c++)在实现继承的不同之处在于java只支持单继承,不支持多重继承.即java 中一个类只能继承于另一个类.我们将被继承的类称之为父类(基类),继承类称之 ...

  6. 统计"1"个数问题

    问题: 给定一个十进制整数N,求出从1到N的所有整数中出现”1”的个数. 例如:N=2时 1,2出现了1个 “1” . N=12时 1,2,3,4,5,6,7,8,9,10,11,12.出现了5个“1 ...

  7. hdu 1575 矩阵快速幂模板

    #include "iostream" #include "vector" #include "cstring" using namespa ...

  8. COGS8 备用交换机

    备用交换机 ★★   输入文件:gd.in   输出文件:gd.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间 ...

  9. appium-向右滑动定位

    上面5幅欢迎图,要向右滑动4次再点击[立即体验]才可以到首屏 #首页欢迎图滑动4次 for i in range(4): driver.swipe(1200, 200, 10, 200, 1500) ...

  10. visual studio 2012如何彻底删除TFS上的团队项目

    http://www.cnblogs.com/zfanlong1314/p/3378441.html 本人的TFS地址:https://zfanlong1314.visualstudio.com/ 最 ...