Django 1.6 最佳实践: 如何组织和设置urls.py

在我们的博客中, 记录了我们在开发过程中所使用的技术和遇到的问题, 希望作为其他开发和设计者的一个学习交流平台.

Django 1.6 最佳实践: 如何组织和设置urls.py


在本节中, 我们主要讨论一下urls.py设置的主要原则. 在Django中, 每一个request, 都通过URLConfs(通常是urls.py)指向一个view. 这种松散的耦合使得我们拥有无穷的灵活性. 但是需要注意的是:

  • views.py中应该包含view的逻辑
  • urls.py中应该包含URL逻辑

来看Django tutorial中的一段代码:

    # 不要这么干!
    from django.conf.urls import patterns, url
    from django.views.generic import DetailView

    from tastings.models import Tasting

    urlpatterns = patterns("",
        url(r"^(?<pk>\d+)/$", 
            DetailView.as_view(model=Tasting, 
            template_name="tastings/detail.html"), 
            name="detail"),
        url(r"^(?<pk>\d+)/results/$", 
            DetailView.as_view(model=Tasting, 
            template_name="tastings/results.html"), 
            name="results"),
    )

初看以上代码好像没什么问题, 但是仔细思考之后, 我们会发现他违反了多想Django的哲学:

  • views, urls和models之间的松散耦合性
  • 不重复自己的原则
  • 灵活性原则: class view无法被继承使用
  • 其他: 例如如何增加权限验证, 身份验证等

1. URLConf的松散耦合性

以下是对上面代码的纠正:

    # tastings/views.py
    from django.views.generic import ListView, DetailView, UpdateView
    from django.core.urlresolvers import reverse

    from .models import Tasting

    class TasteListView(ListlView):
        model = Tasting

    class TasteDetailView(DetailView):
        model = Tasting

    class TasteResultsView(TasteDetailView):
        template_name = "tastings/results.html"

    class TasteUpdateView(UpdateView):
        model = Tasting

        def get_success_url(self):
            return reverse("tastes:detail", kwargs={"pk": self.object.pk})
    # tastings/urls.py
    from django.conf.urls import patterns, url

    from .models import views

    urlpatterns = patterns("",
        url(
            regex=r"^$",
            view=views.TasteListView.as_view(),
            name="list"
        ),
        url(
            regex=r"^(?P<pk>\d+)/$",
            view=views.TasteDetailView.as_view(),
            name="detail"
        ),
        url(
            regex=r"^(?P<pk>\d+)/results/$",
            view=views.TasteResultsView.as_view(),
            name="results"
        ),
        url(
            regex=r"^(?P<pk>\d+)/update/$",
            view=views.TasteUpdateView.as_view(),
            name="update"
        )
    )

看了以上代码, 你可能会问, 这真的是正确的做法吗? 你不仅使用了两个文件, 而且代码的行数也增加了! 以下是我们做出这些修改的原因:

  • 符合"不重复自己"的原则: 在views中, 没有重复的参数(包括argument和attribute)
  • 符合松散耦合原则: 我们将model和template的从URLConf中分离了出来, 使得我们有可能从一个或多个URLConf中调用view
  • URLConf应当只做一件事, 且要把它做好: 修改后的URLConf只做一件事情, 就是URL routing, 并且我们没有把view中的逻辑混入URLConf中
  • 使用Class-based view之后, 使我们能继承view: 这意味着, 如果需要增加权限验证, 或其他逻辑都将会变得简单易做
  • 增加了灵活性: 通过建立view class, view逻辑的扩展变得更为便捷

2. 使用 URl Namespaces

URL namespaces的作用是, 为URL提供了额外的标签. 表面上看来, 这样做对我们帮助不大, 但是, 一旦你开始使用它们之后, 你就会想, "我为什么 不早点使用URL namespaces呢?". URL namespaces使 tastings_detail 这样的URL变为 tastings:detail.

还是借助以上tasting app的代码, 以下是项目根urls.py中的设置:

    # 项目根urls.py
    from django.conf.urls import patterns, include, url

    urlpatterns += patterns("",
        url(r"^tastings/", include("tastings.urls", namespaces="tastings")),
    )

此时, 在python代码中就可以使用namaspaces了:

    # 截取自上面tastings/views.py
    class TasteUpdateView(UpdateView):
        model = Tasting

        def get_success_url(self):
            return reverse("tastes:detail", kwargs={"pk": self.object.pk})

在template中使用:

    ...
    <ul>
    <% for tasting in tastings %>
        <li>
            <a href="{% url "tastings:detail" tasting.pk %}">{{ tasting.title }}</a>
        </li>
    <% endfor %>
    ...

以上就是如何使用URL namespaces. 下面我们解释一下为什么使用URL namespaces:

第一, 每个app中的url name不需要再使用"appname_detail", "appname_results"等形式的名字了, 只需要使用"detail", 和"results". 因为 有了URL namespaces之后, 可以使用namespace加以区分.

第二, 避免了与第三方库冲突的可能. 如果有第三方app与你的url name冲突, 那么, 我们也可以简单地通过修改namespace来解决冲突.


原文链接: http://weiguda.com/blog/9/