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

2024-06-16 18:07:45 发布

您现在位置:Python中文网/ 问答频道 /正文

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对象,并向上下文数据添加一些内容。在

然后我把这门课抄了17遍。每次,都会有不同的模板,不同的slug,以及添加到上下文数据中的不同内容。在

其想法是有一个所见即所得编辑器,供管理员更改web内容,以及一个用户身份验证系统,允许多人访问站点内容。基本上,一个超级简单的CMS,所以没有人需要编辑html来更新站点。在

但是我真的希望我可以重构这个,这样我就没有这些几乎相同的18个类了。任何关于我应该从何处着手的建议都是非常欢迎的。在


Tags: self内容datagetreturnobjectdefhtml
3条回答

将所有类压缩为从^{}继承的单个类,就像^{}一样,(还要检查^{})并重写它的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文件夹中。如果是,则使用该模板。在

更新

在仔细研究代码之后,可能是这样的:

在你的主要网址.py

^{pr2}$

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

在模型.py

您可以将type字段添加到Article模型中。类型将指定它是什么类型的文章。例如,您的ChildrenViewYouthViewAdultView的类型都可以是music(因为模板都是音乐,所以我假设它们是这样关联的)。在

ARTICLE_TYPE_CHOICES = (
    (0, 'music'),
    (1, 'weddings'),
    (2, 'outreach'),
    ...
)

class Article(models.Model):
     ...
     type = models.IntegerField(choices=ARTICLE_TYPE_CHOICES)
     ...

那么,在你的视图.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的文章实例,Django将查找名为ministry/children.html的模板和名为music.html的模板。在

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

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

一个快速的方法我认为: 在模型中添加一个带有预定义模板选项列表的模板字段(这些选项可以动态创建)。 重写默认的DetailView方法,重写get_template_names方法以将正确的模板分配给视图(如果没有可用的回退,可以通过try:except:)完成。 除此之外,您可以使用任何类型的模型标志来更改视图行为。 这样,您就可以为模型提供一个单一的入口点,而不是到处定义可重复的视图。 我倾向于保持FrontPageView独立于其他视图,这是为了方便起见,因为它有不同的用途。 如果需要可重复的上下文条目,请考虑上下文处理器;如果需要特定视图的可重复上下文条目,请考虑mixin。在

我很少能找到一个我需要使用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')

相关问题 更多 >