【问题标题】:Django - Tips to avoid repeating code in viewsDjango - 避免在视图中重复代码的提示
【发布时间】:2010-03-20 09:41:08
【问题描述】:

我正在通过 python 从 PHP 背景转向 Django 开发,主要是为了处理我认为最有意义的 MVC(或 MVT),尽管在这种模式下我已经开始注意到很多在我看来重复的代码。

例如,当登录时,我有关于用户的信息,我希望出现在每个页面上,尽管在使用 render_to_response 并且在每个视图中这是必需的,但我必须获取信息并将其传递给 render_to_response 函数。

我想知道减少重复代码的最有效方法是什么,这实际上是特定应用程序中所有视图所必需的。

提前致谢。

【问题讨论】:

    标签: django views dry repeat


    【解决方案1】:

    就我个人而言,我是装饰器的忠实粉丝,装饰器是一种非 Django 特有的 Python 功能。装饰器是高阶函数之上的完美语法糖,它们对于减少视图中的样板特别有用——您可以快速定义一个通用的包装器函数,您可以在其中放置重复的代码以便于重用和方便-停止重构。

    向您展示可能比解释它们的工作原理更容易。这是一个简化的视图示例:

    def listpage(request):
        return HttpResponse(render_to_string("itemlist.html", {
            "items": Item.objects.filter(visible=True).order_by("-modifydate")
        }))
    
    def itemlist_tags(request, tags):
        return HttpResponse(render_to_string("itemlist.html", {
            "items": Item.objects.tagged(name=tags).filter(visible=True).order_by("-modifydate"),
        }))
    

    ...但是你说你想让这些页面要求用户登录。你可以像这样添加登录代码:

    def listpage(request):
        if not request.user.is_authenticated():
            return f(request, *args, **kwargs)
        else:
            return HttpResponse(render_to_string("itemlist.html", {
                "items": Item.objects.filter(visible=True).order_by("-modifydate")
            }))
    
    def itemlist_tags(request, tags):
        if not request.user.is_authenticated():
            return f(request, *args, **kwargs)
        else:
            return HttpResponse(render_to_string("itemlist.html", {
                "items": Item.objects.tagged(name=tags).filter(visible=True).order_by("-modifydate"),
            }))
    

    ...它开始变得明显更大和重复,即使是一个人为的例子。你可以用装饰器让你的函数再次变得苗条,就像这样:

    从装饰器导入装饰器

    @decorator
    def loginrequired(f, request, *args, **kwargs):
        if request.user.is_authenticated():
            return f(request, *args, **kwargs)
        else:
            return HttpResponseRedirect("/")
    
    @loginrequired
    def listpage(request):
        return HttpResponse(render_to_string("itemlist.html", {
            "items": Item.objects.filter(visible=True).order_by("-modifydate")
        }))
    
        @loginrequired
    def itemlist_tags(request, tags):
        return HttpResponse(render_to_string("itemlist.html", {
            "items": Item.objects.tagged(name=tags).filter(visible=True).order_by("-modifydate"),
        }))
    
    @loginrequired
    def another_such_function(request):
        (...)
    
    @loginrequired
    def and_again(request):
        (...)
    

    装饰器函数在函数定义时执行。在我的示例中,“f”是一个对象,表示应用了装饰器的函数,您可以以无休止的方式对其进行操作。

    这需要decorator library,它在 PyPI 上是免费的,你会发现许多好的 python 小点心。

    您不需要 this 库来编写装饰器函数,但它很有帮助,尤其是在开始时。他们可以做更多的事情——任何可调用对象都可以是装饰器;您可以装饰类方法并拦截self 变量;装饰器可以链接起来,如下所示:

    @second
    @first
    def originalfunction(*args):
        (...)
    

    如果这个想法能激起你的兴趣,我将把对如此简单的高阶函数操作可以做什么的探索留给你。对于您或任何其他好奇的新 python 爱好者,我还有更多示例。祝你好运。

    【讨论】:

    • 顺便说一下,第二个假视图中的 'tagged()' 函数不是拼写错误;这是我写给 django-tagging 应用程序的简化界面,也是以样板减少的名义,好奇的可以在这里找到:djangosnippets.org/snippets/1942
    • 非常有帮助,谢谢,装饰器似乎对我有很多额外的用途。
    【解决方案2】:

    将常用代码封装在一个函数中,从不同的视图调用。听起来微不足道,但它可以满足 99% 的此类需求。

    要获得更具体的答案,您必须展示要运行的代码的更具体示例。

    【讨论】:

      【解决方案3】:

      有两种主要方法可以抽象出常见的内容。

      Context processors 最适合传递您知道在每个视图中都需要的数据位。

      Template tags - 尤其是包含标签 - 对于呈现在多个模板上相同的页面的不同区域非常有用。

      【讨论】:

        【解决方案4】:

        另外,不要忘记generic views!在 90% 的情况下,您可以包装 object_list 或 object_detail 并为自己节省一些代码。

        【讨论】:

          猜你喜欢
          • 2018-07-07
          • 1970-01-01
          • 2014-03-07
          • 2020-03-09
          • 2011-09-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多