The Django Book

Chapter 8: Advanced Views and URLconfs

第八章 高级视图和URL配置

In Chapter 3, we explained the basics of Django view functions and URLconfs. This chapter goes into more detail about advanced functionality in those two pieces of the framework.


URLconf Tricks

URLconf 技巧

Theres nothing special about URLconfs like anything else in Django, theyre just Python code. You can take advantage of this in several ways, as described in the sections that follow.

psiOnM <a href=”“>nbrzcxnliggw</a>, [url=]nbeqcslpbjht[/url], [link=]xmxroewwggzy[/link],

Streamlining Function Imports

Yfphr6 <a href=”“>wkchavkykyhr</a>, [url=]muokhwblpfjl[/url], [link=]ploxqieojkeb[/link],

Consider this URLconf, which builds on the example in Chapter 3:

看下这个 URLconf,它是建立在第三章的例子上:

from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead, hours_behind, now_in_chicago, now_in_london

urlpatterns = patterns('',
    (r'^now/$', current_datetime),
    (r'^now/plus(\d{1,2})hours/$', hours_ahead),
    (r'^now/minus(\d{1,2})hours/$', hours_behind),
    (r'^now/in_chicago/$', now_in_chicago),
    (r'^now/in_london/$', now_in_london),

As explained in Chapter 3, each entry in the URLconf includes its associated view function, passed directly as a function object. This means its necessary to import the view functions at the top of the module.

正如第三章中所解释的,在 URLconf 中的每一个入口包括了它所联系的视图函数,直接传入了一个函数对象。这就意味着需要在模块开始处导入视图函数。

But as a Django application grows in complexity, its URLconf grows, too, and keeping those imports can be tedious to manage. (For each new view function, you have to remember to import it, and the import statement tends to get overly long if you use this approach.) Its possible to avoid that tedium by importing the views module itself. This example URLconf is equivalent to the previous one:

但随着 Django 应用变得复杂,它的 URLconf 也在增长,并且维护这些导入可能使得管理变麻烦。(对每个新的view函数,你不得不记住要导入它,并且如果采用这种方法导入语句将变得相当长。)有可能通过导入 views 模块本身来避免这个麻烦。这个 URLconf 示例同上一个是等价的:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^now/$', views.current_datetime),
    (r'^now/plus(\d{1,2})hours/$', views.hours_ahead),
    (r'^now/minus(\d{1,2})hours/$', views.hours_behind),
    (r'^now/in_chicago/$', views.now_in_chicago),
    (r'^now/in_london/$', views.now_in_london),

Django offers another way of specifying the view function for a particular pattern in the URLconf: you can pass a string containing the module name and function name rather than the function object itself. Continuing the ongoing example:

Django 还提供了另一种方法可以在 URLconf 中为某个特别的模式指定视图函数:你可以传入一个包含模块名和函数名的字符串,而不是函数对象本身。继续示例:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^now/$', 'mysite.views.current_datetime'),
    (r'^now/plus(\d{1,2})hours/$', 'mysite.views.hours_ahead'),
    (r'^now/minus(\d{1,2})hours/$', 'mysite.views.hours_behind'),
    (r'^now/in_chicago/$', 'mysite.views.now_in_chicago'),
    (r'^now/in_london/$', 'mysite.views.now_in_london'),

(Note the quotes around the view names. Were using 'mysite.views.current_datetime' with quotes instead of mysite.views.current_datetime .)

(注意视图名前后的引号。应该使用带引号的 'mysite.views.current_datetime' 而不是 mysite.views.current_datetime 。)

Using this technique, its no longer necessary to import the view functions; Django automatically imports the appropriate view function the first time its needed, according to the string describing the name and path of the view function.

使用这个技术,就不必导入视图函数了;Django 会在第一次需要它时导入合适的视图函数,根据字符串所描述的视图函数的名字和路径。

A further shortcut you can take when using the string technique is to factor out a common view prefix. In our URLconf example, each of the view strings starts with 'mysite.views' , which is redundant to type. We can factor out that common prefix and pass it as the first argument to patterns() , like this:

当使用字符串技术时,你可以采用更简化的方式:提取出一个公共视图前缀。在我们的 URLconf 例子中,每一个视图字符串都是以 'mysite.views' 开始的,造成过多的输入。我们可以提取出公共前缀然后把它作为第一个参数传给 patterns() ,如:

from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
    (r'^now/$', 'current_datetime'),
    (r'^now/plus(\d{1,2})hours/$', 'hours_ahead'),
    (r'^now/minus(\d{1,2})hours/$', 'hours_behind'),
    (r'^now/in_chicago/$', 'now_in_chicago'),
    (r'^now/in_london/$', 'now_in_london'),

Note that you dont put a trailing dot ("." ) in the prefix, nor do you put a leading dot in the view strings. Django puts those in automatically.

注意既不要在前缀后面跟着一个点号("." ),也不要在视图字符串前面放一个点号。 Django 会自动处理它们。

With these two approaches in mind, which is better? It really depends on your personal coding style and needs.


Advantages of the string approach are as follows:


  • Its more compact, because it doesnt require you to import the view functions.

N9gqns <a href=”“>wteejridtwke</a>, [url=]lxotonbmgclb[/url], [link=]hkhsettjexoq[/link],

  • It results in more readable and manageable URLconfs if your view functions are spread across several different Python modules.

  • 如果你的视图函数存在于几个不同的 Python 模块的话,它可以使得 URLconf 更易读和管理。

Advantages of the function object approach are as follows:

WdAon8 <a href=”“>gnuhdlcakrdi</a>, [url=]mkbdhjkqvlqt[/url], [link=]rdpcyyxxithz[/link],

  • It allows for easy wrapping of view functions. See the section Wrapping View Functions later in this chapter.

  • 更容易对视图函数进行包装(wrap)。参见本章后面的《包装视图函数》一节。

  • Its more Pythonic that is, its more in line with Python traditions, such as passing functions as objects.

  • 更 Pythonic,更符合 Python 的传统,如把函数当成对象传递。

Both approaches are valid, and you can even mix them within the same URLconf. The choice is yours.

两个方法都是有效的,甚至你可以在同一个 URLconf 中混用它们。决定权在你。

Using Multiple View Prefixes


In practice, if you use the string technique, youll probably end up mixing views to the point where the views in your URLconf wont have a common prefix. However, you can still take advantage of the view prefix shortcut to remove duplication. Just add multiple patterns() objects together, like this:

在实践中,如果你使用字符串技术,特别是当你的 URLconf 中没有一个公共前缀时,你最终可能混合视图。然而,你仍然可以利用视图前缀的简便方式来减少重复。只要增加多个 patterns() 对象,象这样:



from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^/?$', 'mysite.views.archive_index'),
    (r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
    (r'^tag/(\w+)/$', 'weblog.views.tag'),



from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
    (r'^/?$', 'archive_index'),
    (r'^(\d{4})/([a-z]{3})/$', 'archive_month'),

urlpatterns += patterns('weblog.views',
    (r'^tag/(\w+)/$', 'tag'),

All the framework cares about is that theres a module-level variable called urlpatterns . This variable can be constructed dynamically, as we do in this example.

整个框架关注的是存在一个名为 urlpatterns 的模块级别的变量。这个变量可以动态构建,正如本例中我们所做的一样。

Special-Casing URLs in Debug Mode


Speaking of constructing urlpatterns dynamically, you might want to take advantage of this technique to alter your URLconfs behavior while in Djangos debug mode. To do this, just check the value of the DEBUG setting at runtime, like so:

当谈到动态构建 urlpatterns 时,你可能想利用这一技术,在 Django 的调试模式时,来修改 URLconf 的行为。为了做到这一点,只要在运行时检查 DEBUG 配置项的值即可,如:

from django.conf.urls.defaults import*
from django.conf import settings

urlpatterns = patterns('',
    (r'^$', 'mysite.views.homepage'),
    (r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),

if settings.DEBUG:
    urlpatterns += patterns('',
        (r'^debuginfo$', 'mysite.views.debug'),

In this example, the URL /debuginfo/ will only be available if your DEBUG setting is set to True .

在这个例子中,URL /debuginfo/ 将只有在你的 DEBUG 配置项设为 True 时才有效。

Using Named Groups


In all of our URLconf examples so far, weve used simple, non-named regular expression groups that is, we put parentheses around parts of the URL we wanted to capture, and Django passes that captured text to the view function as a positional argument. In more advanced usage, its possible to use named regular expression groups to capture URL bits and pass them as keyword arguments to a view.

到目前为止,在所有 URLconf 例子中,我们使用的很简单,即 无命名 正则表达式组,在我们想要捕获的URL部分上加上小括号,Django 会将捕获的文本作为位置参数传递给视图函数。在更高级的用法中,还可以使用 命名 正则表达式组来捕获URL,并且将其作为 关键字 参数传给视图。

Keyword Arguments vs. Positional Arguments

关键字参数 对比 位置参数

A Python function can be called using keyword arguments or positional arguments and, in some cases, both at the same time. In a keyword argument call, you specify the names of the arguments along with the values youre passing. In a positional argument call, you simply pass the arguments without explicitly specifying which argument matches which value; the association is implicit in the arguments order.

一个 Python 函数可以使用关键字参数或位置参数来调用,在某些情况下,可以同时进行使用。在关键字参数调用中,你要指定参数的名字和传入的值。在位置参数调用中,你只需传入参数,不需要明确指明哪个参数与哪个值对应,它们的对应关系隐含在参数的顺序中。

For example, consider this simple function:


def sell(item, price, quantity):
    print "Selling %s unit(s) of %s at %s" % (quantity, item, price)

To call it with positional arguments, you specify the arguments in the order in which theyre listed in the function definition:


sell('Socks', '$2.50', 6)

To call it with keyword arguments, you specify the names of the arguments along with the values. The following statements are equivalent:


sell(item='Socks', price='$2.50', quantity=6)
sell(item='Socks', quantity=6, price='$2.50')
sell(price='$2.50', item='Socks', quantity=6)
sell(price='$2.50', quantity=6, item='Socks')
sell(quantity=6, item='Socks', price='$2.50')
sell(quantity=6, price='$2.50', item='Socks')

Finally, you can mix keyword and positional arguments, as long as all positional arguments are listed before keyword arguments. The following statements are equivalent to the previous examples:


sell('Socks', '$2.50', quantity=6)
sell('Socks', price='$2.50', quantity=6)
sell('Socks', quantity=6, price='$2.50')

In Python regular expressions, the syntax for named regular expression groups is (?P<name>pattern) , where name is the name of the group and pattern is some pattern to match.

在 Python 正则表达式中,命名的正则表达式组的语法是 (?P<name>pattern) ,这里 name 是组的名字,而 pattern 是匹配的某个模式。

Heres an example URLconf that uses non-named groups:

下面是一个使用无名组的 URLconf 的例子:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^articles/(\d{4})/$', views.year_archive),
    (r'^articles/(\d{4})/(\d{2})/$', views.month_archive),

Heres the same URLconf, rewritten to use named groups:

ZNpmIL <a href=”“>qucosvxmznkl</a>, [url=]yjdxzjajhrsz[/url], [link=]zreyikrpzidz[/link],

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^articles/(?P<year>\d{4})/$', views.year_archive),
    (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),

This accomplishes exactly the same thing as the previous example, with one subtle difference: the captured values are passed to view functions as keyword arguments rather than positional arguments.

这段代码和前面的功能完全一样,只有一个细微的差别:把提取的值用命名参数的方式 传递给视图函数,而不是用按顺序的匿名参数的方式。

For example, with non-named groups, a request to /articles/2006/03/ would result in a function call equivalent to this:

例如,如果不带命名组,请求 /articles/2006/03/ 将会 等于这样的函数调用:

month_archive(request, '2006', '03')

With named groups, though, the same request would result in this function call:


month_archive(request, year='2006', month='03')

In practice, using named groups makes your URLconfs slightly more explicit and less prone to argument-order bugs and you can reorder the arguments in your views function definitions. Following the preceding example, if we wanted to change the URLs to include the month before the year, and we were using non-named groups, wed have to remember to change the order of arguments in the month_archive view. If we were using named groups, changing the order of the captured parameters in the URL would have no effect on the view.

使用命名组可以让你的URLconfs更加清晰,减少参数次序可能搞混的潜在BUG,还可以 让你在函数定义中对参数重新排序。接着上面这个例子,如果我们想修改URL把月份放到 年份的 前面 ,而不使用命名组的话,我们就不得不去修改视图 month_archive 的参数次序。如果我们使用命名组的话,修改URL里提取参数的次序对视图没有影响。

Of course, the benefits of named groups come at the cost of brevity; some developers find the named-group syntax ugly and too verbose. Still, another advantage of named groups is readability, especially by those who arent intimately familiar with regular expressions or your particular Django application. Its easier to see whats happening, at a glance, in a URLconf that uses named groups.

当然,命名组的代价就是失去了简洁性:一些开发者觉得命名组的语法丑陋和显得冗余。 命名组的另一个好处就是可读性强,特别是熟悉正则表达式或自己开发的Django 应用的开发者。看一眼URLconf里的这些命名组就知道这是干什么用的了。

Understanding the Matching/Grouping Algorithm


A caveat with using named groups in a URLconf is that a single URLconf pattern cannot contain both named and non-named groups. If you do this, Django wont throw any errors, but youll probably find that your URLs arent matching as you expect. Specifically, heres the algorithm the URLconf parser follows, with respect to named groups vs. non-named groups in a regular expression:


  • If there are any named arguments, it will use those, ignoring non-named arguments.

  • 如果有任何命名的组,Django会忽略非命名组而直接使用命名组。

  • Otherwise, it will pass all non-named arguments as positional arguments.

  • 否则,Django会把所有非命名组以位置参数的形式传递。

  • In both cases, it will pass any extra options as keyword arguments. See the next section for more information.

  • 在以上的两种情况,Django同时会以关键字参数的方式传递一些额外参数。更具体的信息可参考下一节。

Passing Extra Options to View Functions


Sometimes youll find yourself writing view functions that are quite similar, with only a few small differences. For example, say you have two views whose contents are identical except for the template they use:



from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^foo/$', views.foo_view),
    (r'^bar/$', views.bar_view),


from django.shortcuts import render_to_response
from mysite.models import MyModel

def foo_view(request):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response('template1.html', {'m_list': m_list})

def bar_view(request):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response('template2.html', {'m_list': m_list})

Were repeating ourselves in this code, and thats inelegant. At first, you may think to remove the redundancy by using the same view for both URLs, putting parentheses around the URL to capture it, and checking the URL within the view to determine the template, like so:



from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^(foo)/$', views.foobar_view),
    (r'^(bar)/$', views.foobar_view),


from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, url):
    m_list = MyModel.objects.filter(is_new=True)
    if url == 'foo':
        template_name = 'template1.html'
    elif url == 'bar':
        template_name = 'template2.html'
    return render_to_response(template_name, {'m_list': m_list})

The problem with that solution, though, is that it couples your URLs to your code. If you decide to rename /foo/ to /fooey/ , youll have to remember to change the view code.

这种解决方案的问题还是老缺点,就是把你的URL耦合进你的代码里面了。如果你打算把 /foo/ 改成 /fooey/ 的话,那么你就得记住要去改变视图里面的代码。

The elegant solution involves an optional URLconf parameter. Each pattern in a URLconf may include a third item: a dictionary of keyword arguments to pass to the view function.


With this in mind, we can rewrite our ongoing example like this:



from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
    (r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),


from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, template_name):
    m_list = MyModel.objects.filter(is_new=True)
    return render_to_response(template_name, {'m_list': m_list})

As you can see, the URLconf in this example specifies template_name in the URLconf. The view function treats it as just another parameter.

如你所见,这个例子中,URLconf指定了 template_name 。而视图函数则会把它处理成另一个参数而已。

This extra URLconf options technique is a nice way of sending additional information to your view functions with minimal fuss. As such, its used by a couple of Djangos bundled applications, most notably its generic views system, which we cover in Chapter 9.


The following sections contain a couple of ideas on how you can use the extra URLconf options technique in your own projects.


Faking Captured URLconf Values


Say you have a set of views that match a pattern, along with another URL that doesnt fit the pattern but whose view logic is the same. In this case, you can fake the capturing of URL values by using extra URLconf options to handle that extra URL with the same view.


For example, you might have an application that displays some data for a particular day, with URLs such as these:


# ...

This is simple enough to deal with you can capture those in a URLconf like this (using named group syntax):


urlpatterns = patterns('',
    (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),

And the view function signature would look like this:


def my_view(request, month, day):
    # ....

This approach is straightforward its nothing you havent seen before. The trick comes in when you want to add another URL that uses my_view but whose URL doesnt include a month and/or day .

这种解决方案很直接,没有用到什么你没见过的技术。问题在于当你想为添加一个使用 my_view 视图的URL但它没有包含一个 month 和/或者 一个 day

For example, you might want to add another URL, /mydata/birthday/ , which would be equivalent to /mydata/jan/06/ . You can take advantage of extra URLconf options like so:

比如你可能会想增加这样一个URL, /mydata/birthday/ , 这个URL等价于 /mydata/jan/06/ 。这时你可以这样利用额外URLconf参数:

urlpatterns = patterns('',
    (r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
    (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),

The cool thing here is that you dont have to change your view function at all. The view function only cares that it gets month and day parameters it doesnt matter whether they come from the URL capturing itself or extra parameters.

在这里最帅的地方莫过于你根本不用改变你的视图函数。视图函数只会关心它 获得monthday 参数,它不会去管这些参数到底是捕捉回来的还是被额外提供的。

Making a View Generic

Its good programming practice to factor out commonalities in code. For example, with these two Python functions:


def say_hello(person_name):
    print 'Hello, %s' % person_name

def say_goodbye(person_name):
    print 'Goodbye, %s' % person_name

we can factor out the greeting to make it a parameter:


def greet(person_name, greeting):
    print '%s, %s' % (greeting, person_name)

You can apply this same philosophy to your Django views by using extra URLconf parameters.


With this in mind, you can start making higher-level abstractions of your views. Instead of thinking to yourself, This view displays a list of Event objects, and That view displays a list of BlogEntry objects, realize theyre both specific cases of A view that displays a list of objects, where the type of object is variable.

了解这个以后,你可以开始创作高抽象的视图。更具体地说,比如这个视图显示一系列的 Event 对象,那个视图显示一系列的 BlogEntry 对象,并意识到它们都是一个用来显示一系列对象的视图的特例,而对象的类型其实就是一个变量。

Take this code, for example:



from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
    (r'^events/$', views.event_list),
    (r'^blog/entries/$', views.entry_list),


from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry

def event_list(request):
    obj_list = Event.objects.all()
    return render_to_response('mysite/event_list.html', {'event_list': obj_list})

def entry_list(request):
    obj_list = BlogEntry.objects.all()
    return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})

The two views do essentially the same thing: they display a list of objects. So lets factor out the type of object theyre displaying:



from django.conf.urls.defaults import *
from mysite import models, views

urlpatterns = patterns('',
    (r'^events/$', views.object_list, {'model': models.Event}),
    (r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),


from django.shortcuts import render_to_response

def object_list(request, model):
    obj_list = model.objects.all()
    template_name = 'mysite/%s_list.html' % model.__name__.lower()
    return render_to_response(template_name, {'object_list': obj_list})

With those small changes, we suddenly have a reusable, model-agnostic view! From now on, anytime we need a view that lists a set of objects, we can simply reuse this object_list view rather than writing view code. Here are a couple of notes about what we did:

就这样小小的改动,我们突然发现我们有了一个可复用的,模型无关的视图!从现在开始,当我们需要一个视图来显示一系列的对象时,我们可以简简单单的重用这一个 object_list 视图,而无须另外写视图代码了。以下是我们做过的事情:

  • Were passing the model classes directly, as the model parameter. The dictionary of extra URLconf options can pass any type of Python object not just strings.

  • 我们通过 model 参数直接传递了模型类。额外URLconf参数的字典是可以传递任何类型的对象,而不仅仅只是字符串。

  • The model.objects.all() line is an example of duck typing : If it walks like a duck and talks like a duck, we can treat it like a duck. Note the code doesnt know what type of object model is; the only requirement is that model have an objects attribute, which in turn has an all() method.

  • 这一行: model.objects.all()鸭子界定 (原文:duck typing,是计算机科学中一种动态类型判断的概念)的一个例子:如果一只鸟走起来像鸭子,叫起来像鸭子,那我们就可以把它当作是鸭子了。需要注意的是代码并不知道 model 对象的类型是什么;它只要求 model 有一个 objects 属性,而这个属性有一个 all() 方法。

  • Were using model.__name__.lower() in determining the template name. Every Python class has a __name__ attribute that returns the class name. This feature is useful at times like this, when we dont know the type of class until runtime. For example, the BlogEntry classs __name__ is the string 'BlogEntry' .

  • 我们使用 model.__name__.lower() 来决定模板的名字。每个Python的类都有一个 __name__ 属性返回类名。这特性在当我们直到运行时刻才知道对象类型的这种情况下很有用。比如, BlogEntry 类的 __name__ 就是字符串 'BlogEntry'

  • In a slight difference between this example and the previous example, were passing the generic variable name object_list to the template. We could easily change this variable name to be blogentry_list or event_list , but weve left that as an exercise for the reader.

  • 这个例子与前面的例子稍有不同,我们传递了一个通用的变量名给模板。当然我们可以轻易的把这个变量名改成 blogentry_list 或者 event_list ,不过我们打算把这当作练习留给读者。

Because database-driven Web sites have several common patterns, Django comes with a set of generic views that use this exact technique to save you time. We cover Djangos built-in generic views in the next chapter.


Giving a View Configuration Options

If youre distributing a Django application, chances are that your users will want some degree of configuration. In this case, its a good idea to add hooks to your views for any configuration options you think people may want to change. You can use extra URLconf parameters for this purpose.


A common bit of an application to make configurable is the template name:


def my_view(request, template_name):
    var = do_something()
    return render_to_response(template_name, {'var': var})
Understanding Precedence of Captured Values vs. Extra Options

When theres a conflict, extra URLconf parameters get precedence over captured parameters. In other words, if your URLconf captures a named-group variable and an extra URLconf parameter includes a variable with the same name, the extra URLconf parameter value will be used.


For example, consider this URLconf:


from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),

Here, both the regular expression and the extra dictionary include an id . The hard-coded id gets precedence. That means any request (e.g., /mydata/2/ or /mydata/432432/ ) will be treated as if id is set to 3 , regardless of the value captured in the URL.

这里,正则表达式和额外字典都包含了一个 id 。硬编码的(额外字典的) id 将优先使用。就是说任何请求(比如, /mydata/2/ 或者 /mydata/432432/ )都会作 id 设置为 3 对待,不管URL里面能捕捉到什么样的值。

Astute readers will note that in this case, its a waste of time and typing to capture the id in the regular expression, because its value will always be overridden by the dictionarys value. Thats correct; we bring this up only to help you avoid making the mistake.

聪明的读者会发现在这种情况下,在正则表达式里面写上捕捉是浪费时间的,因为 id 的值总是会被字典中的值覆盖。没错,我们说这个的目的只是为了让你不要犯这样的错误。

Using Default View Arguments


Another convenient trick is to specify default parameters for a views arguments. This tells the view which value to use for a parameter by default if none is specified.


Heres an example:



from django.conf.urls.defaults import *

urlpatterns = patterns('',


def page(request, num="1"):
    # Output the appropriate page of blog entries, according to num.
    # ...

Here, both URL patterns point to the same view but the first pattern doesnt capture anything from the URL. If the first pattern matches, the page() function will use its default argument for num , "1" . If the second pattern matches, page() will use whatever num value was captured by the regular expression.

在这里,两个URL表达式都指向了同一个视图 ,但是第一个表达式没有传递任何参数。如果匹配到了第一个样式, page() 函数将会对参数 num 使用默认值 "1" ,如果第二个表达式匹配成功, page() 函数将使用正则表达式传递过来的num的值。

Its common to use this technique in conjunction with configuration options, as explained earlier. This example makes a slight improvement to the example in the Giving a View Configuration Options section by providing a default value for template_name :

就像前面解释的一样,这种技术与配置选项的联用是很普遍的。以下这个例子比提供视图配置选项一节中的例子有些许的改进。它为 template_name 提供了一个默认值:

def my_view(request, template_name='mysite/my_view.html'):
    var = do_something()
    return render_to_response(template_name, {'var': var})

Special-Casing Views


Sometimes youll have a pattern in your URLconf that handles a large set of URLs, but youll need to special-case one of them. In this case, take advantage of the linear way a URLconf is processed and put the special case first.

有时你有一个模式来处理在你的URLconf中的一系列URL,但是有时候需要特别处理其中的某个URL。在这种情况下,要使用将URLconf中把特殊情况放在首位的线性处理方式 。

For example, the add an object pages in Djangos admin site are represented by this URLconf line:


urlpatterns = patterns('',
    # ...
    ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
    # ...

This matches URLs such as /myblog/entries/add/ and /auth/groups/add/ . However, the add page for a user object (/auth/user/add/ ) is a special case it doesnt display all of the form fields, it displays two password fields, and so forth. We could solve this problem by special-casing in the view, like so:

这将匹配像 /myblog/entries/add//auth/groups/add/ 这样的URL 。然而,对于用户对象的添加页面( /auth/user/add/ )是个特殊情况,因为它不会显示所有的表单域,它显示两个密码域等等。我们 可以 在视图中特别指出以解决这种情况:

def add_stage(request, app_label, model_name):
    if app_label == 'auth' and model_name == 'user':
        # do special-case code
        # do normal code

but thats inelegant for a reason weve touched on multiple times in this chapter: it puts URL logic in the view. As a more elegant solution, we can take advantage of the fact that URLconfs are processed in order from top to bottom:


urlpatterns = patterns('',
    # ...
    ('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
    ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
    # ...

With this in place, a request to /auth/user/add/ will be handled by the user_add_stage view. Although that URL matches the second pattern, it matches the top one first. (This is short-circuit logic.)

在这种情况下,象 /auth/user/add/ 的请求将会被 user_add_stage 视图处理。尽管URL也匹配第二种模式,它会先匹配上面的模式。(这是短路逻辑。)

Capturing Text in URLs


Each captured argument is sent to the view as a plain Python string, regardless of what sort of match the regular expression makes. For example, in this URLconf line:


(r'^articles/(?P<year>\d{4})/$', views.year_archive),

the year argument to views.year_archive() will be a string, not an integer, even though \d{4} will only match integer strings.

尽管 \d{4} 将只匹配整数的字符串,但是参数 year 是作为字符串传至 views.year_archive() 的,而不是整型。

This is important to keep in mind when youre writing view code. Many built-in Python functions are fussy (and rightfully so) about accepting only objects of a certain type. A common error is to attempt to create a object with string values instead of integer values:

当你在写视图代码时记住这点很重要,许多Python内建的方法对于接受的对象的类型很讲究。一个典型的的错误就是用字符串值而不是整数值来创建 对象:

>>> import datetime
>>>'1993', '7', '9')
Traceback (most recent call last):
TypeError: an integer is required
>>>, 7, 9), 7, 9)

Translated to a URLconf and view, the error looks like this:



from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),


import datetime

def day_archive(request, year, month, day)
    # The following statement raises a TypeError!
    date =, month, day)

Instead, day_archive() can be written correctly like this:

因此, day_archive() 应该这样写才是正确的:

def day_archive(request, year, month, day)
    date =, int(month), int(day))

Note that int() itself raises a ValueError when you pass it a string that is not composed solely of digits, but were avoiding that error in this case because the regular expression in our URLconf has ensured that only strings containing digits are passed to the view function.

注意,当你传递了一个并不完全包含数字的字符串时, int() 会抛出 ValueError 的异常,不过我们已经避免了这个错误,因为在URLconf的正则表达式中已经确保只有包含数字的字符串才会传到这个视图函数中。

Determining What the URLconf Searches Against


When a request comes in, Django tries to match the URLconf patterns against the requested URL, as a normal Python string (not as a Unicode string). This does not include GET or POST parameters, or the domain name. It also does not include the leading slash, because every URL has a leading slash.

当一个请求进来时,Django试着将请求的URL作为一个普通Python字符串进行URLconf模式匹配(而不是作为一个Unicode字符串)。这并不包括 GETPOST 参数或域名。它也不包括第一个斜杠,因为每个URL必定有一个斜杠。

For example, in a request to , Django will try to match myapp/ . In a request to , Django will try to match myapp/ .

例如,在向 的请求中,Django将试着去匹配 myapp/ 。在向 的请求中,Django同样会去匹配 myapp/

The request method (e.g., POST , GET , HEAD ) is not taken into account when traversing the URLconf. In other words, all request methods will be routed to the same function for the same URL. Its the responsibility of a view function to perform branching based on request method.

在解析URLconf时,请求方法(例如, POSTGETHEAD )并 不会 被考虑。换而言之,对于相同的URL的所有请求方法将被导向到相同的函数中。因此根据请求方法来处理分支是视图函数的责任。

Including Other URLconfs


If you intend your code to be used on multiple Django-based sites, you should consider arranging your URLconfs in such a way that allows for including.


At any point, your URLconf can include other URLconf modules. This essentially roots a set of URLs below other ones. For example, this URLconf includes other URLconfs:


from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^weblog/', include('')),
    (r'^photos/', include('')),
    (r'^about/$', 'mysite.views.about'),

Theres an important gotcha here: the regular expressions in this example that point to an include() do not have a $ (end-of-string match character) but do include a trailing slash. Whenever Django encounters include() , it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing.

这里有个很重要的地方:例子中的指向 include() 的正则表达式并 包含一个 $ (字符串结尾匹配符),但是包含了一个斜杆。每当Django遇到 include() 时,它将截断匹配的URL,并把剩余的字符串发往包含的URLconf作进一步处理。

Continuing this example, heres the URLconf :


from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^(\d\d\d\d)/$', ''),
    (r'^(\d\d\d\d)/(\d\d)/$', ''),

With these two URLconfs, heres how a few sample requests would be handled:


  • /weblog/2007/ : In the first URLconf, the pattern r'^weblog/' matches. Because it is an include() , Django strips all the matching text, which is 'weblog/' in this case. The remaining part of the URL is 2007/ , which matches the first line in the URLconf.

  • /weblog/2007/ :在第一个URLconf中,模式 r'^weblog/' 被匹配。因为它是一个 include() ,Django将截掉所有匹配的文本,在这里是 'weblog/' 。URL剩余的部分是 2007/ , 将在 这个URLconf的第一行中被匹配到。

  • /weblog//2007/ : In the first URLconf, the pattern r'^weblog/' matches. Because it is an include() , Django strips all the matching text, which is 'weblog/' in this case. The remaining part of the URL is /2007/ (with a leading slash), which does not match any of the lines in the URLconf.

  • /weblog//2007/ :在第一个URLconf中,模式 r'^weblog/' 被匹配。因为它是一个 include() ,Django将截掉所有匹配的文本,在这里是 'weblog/' 。URL剩余的部分是 /2007/ (开头有一个斜杠),将不会匹配 中的任何URLconf。

  • /about/ : This matches the view mysite.views.about in the first URLconf, demonstrating that you can mix include() patterns with non-include() patterns.

  • /about/ : 这个匹配第一个URLconf中的 mysite.views.about 视图。只是为了示范你可以混合 include() patterns和 non-include() patterns在一起使用。

How Captured Parameters Work with include()


An included URLconf receives any captured parameters from parent URLconfs, for example:

一个被包含的URLconf接收任何来自parent URLconfs的被捕获的参数,比如:

# root

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^(?P<username>\w+)/blog/', include('')),

# foo/urls/

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$', 'foo.views.blog_index'),
    (r'^archive/$', 'foo.views.blog_archive'),

In this example, the captured username variable is passed to the included URLconf and, hence, to every view function within that URLconf.

在这个例子中,被捕获的 username 变量将传递给被包含的 URLconf,进而传递给那个URLconf中的 每一个 视图函数。

Note that the captured parameters will always be passed to every line in the included URLconf, regardless of whether the lines view actually accepts those parameters as valid. For this reason, this technique is useful only if youre certain that every view in the included URLconf accepts the parameters youre passing.

注意,这个被捕获的参数 总是 传递到被包含的URLconf中的 每一 行,不管那些行对应的视图是否需要这些参数。因此,这个技术只有在你确实需要那个被传递的参数的时候才显得有用。

How Extra URLconf Options Work with include()


Similarly, you can pass extra URLconf options to include() , just as you can pass extra URLconf options to a normal view as a dictionary. When you do this, each line in the included URLconf will be passed the extra options.

相似的,你可以传递额外的URLconf选项到 include() , 就像你可以通过字典传递额外的URLconf选项到普通的视图。当你这样做的时候,被包含URLconf的 每一 行都会收到那些额外的参数。

For example, the following two URLconf sets are functionally identical.


Set one:



from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^blog/', include('inner'), {'blogid': 3}),


from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive'),
    (r'^about/$', 'mysite.views.about'),
    (r'^rss/$', 'mysite.views.rss'),

Set two:



from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^blog/', include('inner')),


from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
    (r'^about/$', 'mysite.views.about', {'blogid': 3}),
    (r'^rss/$', 'mysite.views.rss', {'blogid': 3}),

As is the case with captured parameters (explained in the previous section), extra options will always be passed to every line in the included URLconf, regardless of whether the lines view actually accepts those options as valid. For this reason, this technique is useful only if youre certain that every view in the included URLconf accepts the extra options youre passing.

这个例子和前面关于被捕获的参数一样(在上一节就解释过这一点),额外的选项将 总是 被传递到被包含的URLconf中的 每一 行,不管那一行对应的视图是否确实作为有效参数接收这些选项,因此,这个技术只有在你确实需要那个被传递的额外参数的时候才显得有用。

Whats Next?


One of Djangos main goals is to reduce the amount of code developers need to write, and in this chapter we suggested how to cut down the code of your views and URLconfs.


The next logical step in code elimination is removing the need to write views entirely. Thats the topic of the next chapter.


Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by 粤ICP备16122281号-1