django + django-cms 开发实战-django-cms插件开发

django的设计处处体现了高内聚,低耦合的设计思想。比如APP的设计,可轻松从一个project移植到另一个project中。django-cms内部各种功能也是插件的方式提供。
初次安装的django-cms,可能只有一个现呈的page模块,如果你需要更高级的功能还是同样需要设计APP。django-cms设计了一些方法可以很方便让我们自己的APP与Page一起工作。

编写插件

我们已经有了之前设计的Products APP,他有自己的模板,有自己访问的URL,但如果我想在首页显示我置顶的产品,那就需要制作一个django-cms的插件了。

django-cms的插件中把需要记录的参数(比如我要显示哪些分类共多少条置顶的产品)做为model, 在django-cms为placeholder添加你的插件时,这个model中的项会出现在界面上供你修改:

from cms.models import CMSPlugin
class ProductPlugin(CMSPlugin):
    # 使用多对多关系admin中对应多选控件
    cates = models.ManyToManyField(Categroy, related_name='plugins')
    number = models.IntegerField(default = 6)

    def __unicode__(self):
      # 对于多对多的字段内部读取时可以用.all()来访问
      return "%s [%s]" % ("|".join([t.title for t in self.cates.all()]), self.number)

有了这个model然后创建真正的插件文件cms_plugins.py:

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from products.models import ProductsPlugin as ProductsPluginModel
from products.models import Product, Categroy
from django.utils.translation import ugettext as _

class ProductsPlugin(CMSPluginBase):
    model = ProductsPluginModel
    name = _("Products Top Plugin")
    render_template = "products/plugin_top.html"

    def render(self, context, instance, placeholder):
        pros = Product.objects.filter(categroy__in = instance.cates.all()).filter(is_top = True)[:instance.number]
        context.update({'instance':instance, "products" : pros})
        # context会传递到 render_template定义的模版中
        return context

# 注册到django-cms的插件池中
plugin_pool.register_plugin(ProductsPlugin)

此时django-cms的page栏目里,你就可以为placeholder中添加名为’Products Top Plugin’的自定义插件了。

编写应用钩子

之前在project中的url里添加了一条正则把路径串联到APP中:

url(r'^products/', include("products.urls")),

如果需要django-cms中任意page自定义的页面进入我们的APP如何做到呢? 那需在制作一个app的钩子。

from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _

class ProductsApp(CMSApp):
    name = _("Products App")
    urls = ["products.urls"]

apphook_pool.register(ProductsApp)

有了这个,就可以把project中urls.py里的上述那条url正则去掉了。在创建的page的高级选项中,为”Application”选择我们创建的 “Products App”,那么访问这个page 时会自动进入products的url匹配。

菜单

django-cms中的菜单是可以自己定义的,你可以把任一page页面加入到菜单中,然后通过 ‘show_menu’ 标签调用。那我们自己编写的APP如何加入到菜单中呢。特别时APP也有子菜单的话,如何也显示这些子菜单?那需要 menu.py 出场了。

from cms.menu_bases import CMSAttachMenu
from menus.base import Menu, NavigationNode
from menus.menu_pool import menu_pool
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from products.models import Categroy

class ProductsMenu(CMSAttachMenu):
    name = _("Product Menu")
    def get_nodes(self, request):
        nodes = []
        for cate in Categroy.objects.all():
            node = NavigationNode(
                cate.title,
                reverse('products.views.categroy', args=(cate.path,)),
                cate.id,
                cate.parent and cate.parent.id or None # 有没有父节点
            )
            nodes.append(node)
        return nodes

menu_pool.register_menu(ProductsMenu)

ProductsMenu还需要添加到 cms_app.py中:

class ProductsApp(CMSApp):
    name = _("Products App")
    urls = ["products.urls"]
    # 添加到这里
    menu = ["products.menu"]

接下来的工作是告诉django这个菜单显示在哪个page下面。可以在page的高级设置中的 “Attached menu” 进行选择,最后就剩下在模板中调用了 ‘load menu_tags’并使用标签 ‘show_menu 0 100 100 100’ 显示所有菜单项。

show_menu的参数具体说明可参看: https://django-cms.readthedocs.org/en/2.3.5/getting_started/navigation.html#show-menu

好了,以上就是这次django-cms学习的一些记录,大致上可以应付自己的简单要求了,整个过程感觉django的设计非常巧妙,很多地方为程序员节省了很多工作。django-cms也充满DIY精神,为千变万化的定制需求提供了条件。

参考: