首页 文章

重构基于Django类的视图,清理18个重复的类 .

提问于
浏览
4

https://github.com/AnthonyBRoberts/fcclincoln/blob/master/apps/story/views.py

我有点不好意思承认这是我的 . 但它是 .

class FrontpageView(DetailView):
    template_name = "welcome_content.html"
    def get_object(self):
        return get_object_or_404(Article, slug="front-page")
    def get_context_data(self, **kwargs):
        context = super(FrontpageView, self).get_context_data(**kwargs)
        context['slug'] = "front-page"
        events = Article.objects.filter(slug="events")
        context['events'] = events
        return context

所以这是Django中一个非常普通的基于类的详细信息视图 .

它正在分配模板,获取Article对象,并向context_data添加一些内容 .

然后我复制了这个课17次 . 每次都有一个不同的模板,一个不同的slug,以及添加到context_data的不同内容 .

我们的想法是,管理员可以使用WYSIWYG编辑器来更改Web内容,还有一个用户身份验证系统,允许多人访问网站内容 . 基本上,一个超级简单的CMS,所以没有人必须编辑html来更新网站 .

但我真的希望我可以重构这个,所以我没有这几乎相同的18个 class . 关于我应该从哪里开始的任何建议都是非常受欢迎的 .

3 回答

  • 6

    将所有类压缩到一个继承自TemplateResponseMixin的类,如DetailView,(也检查SingleObjectTemplateResponseMixin)并覆盖其 get_template_names() 方法以返回适合当前情况的模板 .

    正在使用的一个很好的例子是django-blog-zinnia项目

    def get_template_names(self):
        """
        Return a list of template names to be used for the view.
        """
        model_type = self.get_model_type()
        model_name = self.get_model_name()
    
        templates = [
            'zinnia/%s/%s/entry_list.html' % (model_type, model_name),
            'zinnia/%s/%s_entry_list.html' % (model_type, model_name),
            'zinnia/%s/entry_list.html' % model_type,
            'zinnia/entry_list.html']
    
        if self.template_name is not None:
            templates.insert(0, self.template_name)
    
        return templates
    

    Django将获取该名称列表并尝试每个项目以查看它是否存在于templates文件夹中 . 如果是,则使用该模板 .

    更新

    在仔细查看您的代码之后,可能是这样的:

    在主urls.py中

    # convert each url
    url(r'^$', FrontpageView.as_view()),
    url(r'^history/$', HistoryView.as_view()),
    url(r'^calendar/$', CalendarView.as_view()),
    url(r'^news/$', NewsView.as_view()),
    url(r'^visitors/$', VisitorsView.as_view()),
    ...
    # to just
    url(r'^(?P<slug>[\w\d/-]+)/$', SuperSpecialAwesomeView.as_view()),
    # but, put this at the end of urls list after any routes that don't use this view
    

    DetailView ,在设置了类属性 model 之后,将检查 slug 是否在url的kwargs中,如果是,它将使用slug进行模型查找,就像你正在做的那样: Article.ojects.get(slug=self.kwargs['slug'])

    models.py

    您可以在 Article 模型中添加 type 字段 . 该类型将指定它是什么类型的文章 . 例如,您的 ChildrenViewYouthViewAdultView 都可以具有 music 类型(因为模板都是音乐,我们'm assuming that'是如何相关的) .

    ARTICLE_TYPE_CHOICES = (
        (0, 'music'),
        (1, 'weddings'),
        (2, 'outreach'),
        ...
    )
    
    class Article(models.Model):
         ...
         type = models.IntegerField(choices=ARTICLE_TYPE_CHOICES)
         ...
    

    然后,在views.py中

    class SuperSpecialAwesomeView(DetailView):
        template_name = None
        model = Article
        def get_template_names(self):
            slug = self.kwargs.get('slug', '')
            templates = [
                # create a template based on just the slug
                '{0}.html'.format(slug),
                # create a template based on the model's type
                '{0}.html'.format(self.object.get_type_display()),
            ]
            # Allow for template_name overrides in subclasses
            if self.template_name is not None:
                templates.insert(0, self.template_name)
    
            return templates
    

    给定一个类型为 music 的文章实例和一个 ministry/children 的slug,Django将查找名为 ministry/children.html 的模板和名为 music.html 的模板 .

    如果你需要为其他视图做一些特殊的事情(比如你可能需要为 SermonsView ),那么子类 SuperSpecialAwesomeView

    class SermonsView(SuperSpecialAwesomeView):
        paginate_by = 2
        queryset = Article.objects.order_by('-publish_date')
    
  • 0

    我想一个快速的方法:在模型中添加一个模板字段,其中包含一个预定义模板选项列表(可以动态创建) . 覆盖默认的DetailView方法,覆盖get_template_names方法,为视图分配正确的模板(如果没有可用的回退,可以通过try:except :)完成 . 除此之外,您可以使用任何类型的模型标志更改View行为 . 这样,您可以为模型创建单个入口点,而不是在整个位置定义可重复的视图 . 我倾向于保持FrontPageView独立于其他视图,因为它很容易,因为它有不同的用途 . 如果需要可重复的上下文条目,请考虑上下文处理器,如果需要针对特定视图的可重复上下文条目,请考虑使用Mixins .

  • 1

    我很少能找到一个需要使用CBD的地方 .

    你可以像这样重构它:

    def editable_page(slug):
        return {
            'context': {
                'slug': slug
            }
            'template': 'mysupertemplates/{0}.html'.format(slug)
        }
    
    def frontpage(req):
        return editable_page('frontpage')
    
    def chat(req):
        return editable_page('char')
    
    def about(req):
        return editable_page('about')
    

相关问题