django1.8 view(2):URLconf part2

本文翻译自django1.8.2官方文档The view layer中的URLconfs段

URL dispatcher

Including other URLconfs

不管怎么讲,你的urlpatterns能够”包含”其他的URLconf模块.实际上是把其他URL放到”roots”里.
例如,这里有一段Django Web Site自己的URLconf,它包含了一些其他URLconf:

from django.conf.urls import include, url

urlpatterns = [
    # ...snip...
    url(r'^community/', include('django_website.aggrrgator.urls')),
    url(r'^contact/', include('django_website.contact.urls')),
    # ...snip...
]

注意:在这个例子里正则表达式没有$(结尾的匹配符)但是有结尾斜杠.当django遇到include()(django.conf,urls.include()),它把匹配部分去掉,将其他部分发给被包含的URLconf做进一步处理.
另一个方法是使用url()实例来追加到URL patterns.例如,看看这个URLconf:

from django.conf.urls import include, url

from apps.main import views as main_views
from credit import views as credit_views

extra_patterns = [
    url(r'^reports/$', credit_views.report),
    url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
    url(r'^charge/$', credit_views.charge),
]

urlpatterns = [
    url(r'^$', main_views.homepage),
    url(r'^help/', include('apps.help.urls')),
    url(r'^credit/', include(extra_patterns)),
]

在这个例子里,/credit/reports/ URL将会给credit_views.report()视图处理.
当一个pattern前缀被多次使用时,这样做有利于代码重用.例如,看看下面这个URLconf:

from django.conf.urls import url
from . import views

urlpatterns = [
         url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history),    
         url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit),
         url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss),
         url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions),
]

我们可以这样改进:一样的路径前缀值写一次,把后缀组合到一起:

from django.conf.urls import include, url
from . import views

urlpatterns = [
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([
        url(r'^history/$', views.history),
        url(r'^edit/$', views.edit),
        url(r'^discuss/$', views.discuss),
        url(r'^permissions/$', views.permissions),
    ])),
]
Captured patameters(填充参数)

一个被包含的URLconf,能够接受父URLconfs的填充参数,所以下面的例子是有效的:

# In settings/urls/main.py
from django.conf.urls import include, url

urlpatterns = [
    url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
]

# In foo/urls/blog.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.blog.index),
    url(r'^archive/$', views.blog.archive),
]

上面的例子里,填充参数”username”会传递给被包含的URLconf.

Nested arguments(嵌套参数)

正则表达式允许嵌套参数,而且django会解析他们并传递给视图函数.当反转的时候,django会试图填写所有的外面的填充参数,忽略任何嵌套参数.思考下面的例子,URL patterns可以接受一个page参数”

from django.conf.urls import url

urlpatterns = [
    url(r'^blog/(page-(\d+)/)?$', blog_article),  # bad
    url(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments),  # good
]

2个pattern都使用了内嵌参数,而且都会解析:例如,blog/page-2/会匹配给blog_articles,有2个位置参数:page-2/和2.第二个comments的pattern会匹配comments/page-2/,关键字参数page_number的值是2.这个例子里的外层参数是一个non-capturing(不填充)参数(?:…).
The blog_articles view needs the outermost captured argument to be reversed, page-2/ or no arguments in this case, while comments can be reversed with either no arguments or a value for page_number.
嵌套参数在URL和视图参数间创建了强大的耦合就像blog_aiticles:视图函数只接受部分它感兴趣的URL.
This coupling is even more pronounced when reversing, since to reverse the view we need to pass the piece of URL instead of the page number.
一般来说,只有在试图函数需要时才捕获参数的值,当正则表达式需要一个参数但是视图函数不需要时,用non-capturing参数.

Passing extra options to view functions

URLconfs有一个方法能让你用Python字典传递额外的参数给视图函数.
django.conf.urls.url()方法能接受一个可选的第三个参数,用字典表示的二外的关键字参数,传递给视图函数.
例如:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

在这个例子里,请求/blog/2005/时,django会调用views.year_archive(request, year=’2005’, foo=’bar’).
这种技术在syndication framework里用到了.
处理冲突:
可能出现这种情况:有一个URL pattern既捕获关键字参数,又要用字典传递相同名字的额外参数.这种情况发生时,使用字典里的参数.

Passing extra options to include()

同样,你也可以传递额外参数到include().当你传递额外参数到include(),included URLconf里的每行都会传递额外参数.
例如,这2个URLconf集合功能相同:
集合一:

# main.py
from django.conf.urls import include, url

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

# inner.py
from django.conf.urls import url
from mysite import views

urlpatterns = [
    url(r'^archive/$', views.archive),
    url(r'^about/$', views.about),
]

集合二:

# main.py
from django.conf.urls import include, url
from mysite import views

urlpatterns = [
    url(r'^blog/', include('inner')),
]

# inner.py
from django.conf.urls import url

urlpatterns = [
    url(r'^archive/$', views.archive, {'blogid': 3}),
    url(r'^about/$', views.about, {'blogid': 3}),
]

Reverse resolution of URLs

django工程通常有这种需求:在表单中要获得URL,为了嵌入生成的内容中(视图,资源URL,或显示给用户的URL等),或者是服务端网站导览机制(重定向等).
千万不要对这些URL进行硬编码(一个吃力不讨好,不可扩展,容易出错的方案).Equally dangerous is devising ad-hoc mechanisms to generate URLs that are parallel to the design described by the URLconf, which can result in the production of URLs that become stale over time.
也就是说,我们需要DRY机制.他的优势是它允许在不用搜索和替换旧的URL的情况下更新URL设计.
我们能够获得的一个主要的URL的信息是控制视图的标识符(例如name).其他的能够找到正确的URL的信息是视图参数的类型和值.
django提供了如下解决方案:URL映射是唯一存储URL设计的地方.你设置好你的URLconf,就可以在以下目录使用它:
- 通过用户/浏览器开始一个URL请求,它调用对应的django视图,提供从URL中提取的参数.
- Starting with the identification of the corresponding Django view plus the values of arguments that would be passed to it, obtain the associated URL.

第一个方法的使用我们已经在上一节讨论过了.第二个也可以称为reverse resolutin of URLs, reverse URL matching, reverse URL lookup,或者简单的URL reversing.
django提供了执行URL reversing的工具来应对不同层次的需要:
- 在模板里: 使用url模板标签
- 在python代码里: 使用django.core.urlresolvers.reverse()函数
- 在高层次的django模型实例关联到URL处理的代码里: get_absoulte_url()方法.

Examples

思考下这条URLconf:

from django.conf.urls import url
from . import views

urlpatterns = [
    # ...
    url(r'^articles/[0-9]{4}/$', views.year_archive, name='news-year-archive'),
    # ...
]

根据这个设计,和 year nnnn 对应的URL是 /articles/nnnn/.
你可以在模板代码里这样使用它:

<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
{# Or with the year in a template context variable: #}
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>

或者在Python代码里:

from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect

def redirect_to_year(request):
    # ...
    year = 2006
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

如果因为某些原因,决定改变将以年来展示文章的URL,你只需要改URLconf里的一条就行了.
在某些情况下,视图是一个通用视图,在URL和视图间有多对一关系.这时候如果要reversing URL的话,视图名称就不是一个足够好的标识符.请阅读下面一段来了解django提供的解决办法.

Naming URL patterns

为了执行URL reversing,你需要在上面的例子中使用named URL patterns.URL name使用的字符串可以是任何字符.不会受限于python标识符.
当你给URL pattern命名后,确保名称和其他应用的名字没有冲突.如果你将URL pattern命名为comment,而且另一个应用也这么做了,django不能保证你在使用这个名称时哪个会插入到你的模板里.
在你的URL名称上加前缀,可以是应用名,可以减少冲入几率.推荐使用想myapp-comment这样的名称代替comment.

URL namespaces

Introduction

URL namespace允许你唯一地reverse named URL patterns,即使不同的应用使用的相同的URL名称.第三方应用中使用namespace URL是个好习惯(就像在turorial中一样).同样的,它也允许你在一个应用部署多个实例时reverse URL.也就是说,因为一个应用的多个实例共享named URL,namespace提供了分别告诉这些named URL的方法.
django应用可以为一个站点使用URL namespacing,多次部署.例如django.contrib.admin有一个AdminSite类,允许你很容易的部署admin的多个实例.稍后的例子中,我们会讨论如何把tutorial中的polls应用部署在2个地方,以便给2种访问者(authors和publishers)提供相同的服务.
URL namespace有2个部分,都是字符串:

application namespace
这表示部署的应用的名字.每个单独应用的所有实例有同样的应用命名空间.例如,django的admin应用有应用命名空间’admin’.

instance namespace
这表示应用的指定实例.实例命名空间应该是在你的整个项目中唯一的.但是,实例命名空间可以和应用命名空间相同.这常用于指定应用的默认实例.例如,默认的django admin实例有一个实例命名空间’admin’.

Namespaced URL必须使用’:’操作符.例如,admin应用的主页一般用’admin:index’.这表示命名空间’admin’和命名URL’index’.
命名空间可以嵌套.命名URL’sports:polls:index’会在顶级命名空间’sports’里寻找命名空间’polls’,再找一个pattern,叫’index’.

Reversing namespaced URLs

解析命名空间URL(例如,’polls:index’)时,django将整个名字分割成多个,然后试图这样寻找:
1. 首先,django寻找匹配的应用命名空间(在这个例子里是’polls’).这会产生一个应用实例列表.
2. 如果当前的应用已定义,django会找到并返回实例的URL解析器.The current application can be specified as an attribute on the request. Applications that expect to have multiple deployments should set the current_app attribute on the request being processed.
Django 1.8的更改:
In previous versions of Django, you had to set the current_app attribute on any Context or RequestContext that is used to render a template._
The current application can also be specified manually as an argument to the reverse() function.
3. If there is no current application. Django looks for a default application instance. The default application instance is the instance that has an instance namespace matching the application namespace (in this example, an instance of polls called ‘polls’).
4. If there is no default application instance, Django will pick the last deployed instance of the application, whatever its instance name may be.
5. If the provided namespace doesn’t match an application namespace in step 1, Django will attempt a direct lookup of the namespace as an instance namespace.

If there are nested namespaces, these steps are repeated for each part of the namespace until only the view name is unresolved. The view name will then be resolved into a URL in the namespace that has been found.

Example
为了演示在实际操作中的解析策略,思考这个例子:tutorial的polls应用的2个实例,一个叫’author-polls’,一个叫’publisher-polls’.Assume we have enhanced that application so that it takes the instance namespace into consideration when creating and displaying polls.

urls.py

from django. conf.urls import include, url

urlpatterns = [
    url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
    url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls', app_name='polls')),
]

polls/urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
    ...
]

使用这些设定,下面的查找路由就是可能的:
- 如果有一个实例时有效的-也就是说,如果我们在实例’author-polls’里渲染详细页(detail page)-‘polls:index’将会解析到’author-polls’的首页;例如,下面的2个结果都是’/author-polls/’.
在class-based view的方法里:

reverse('polls:index', current_app=self.request.resolver_match.namespace)

在模板里:

{% url 'polls:index' %}

注意在模板里的reversing需要current_app作为属性添加到request里,就像这样:

def render_to_response(self, context, **response_kwargs):
    self.request.current_app = self.request.resolver_match.namespace
    return super(DetailView, self).render_to_response(context, **response_kwargs)
  • If there is no current instance - say, if we were rendering a page somewhere else on the site - ‘polls:index’ will resolve to the last registered instance of polls. Since there is no default instance (instance namespace of ‘polls’), the last instance of polls that is registered will be used. This would be ‘publisher-polls’ since it’s declared last in the urlpatterns.
  • ‘author-polls:index’ will always resolve to the index page of the instance ‘author-polls’ (and likewise for ‘publisher-polls’) .

If there were also a default instance - i.e., an instance named ‘polls’ - the only change from above would be in the case where there is no current instance (the second item in the list above). In this case ‘polls:index’ would resolve to the index page of the default instance instead of the instance declared last in urlpatterns.

URL namespaces and included URLconfs

included URLconf的URL命名空间可以通过2种方式指定.
首先,当你构造你的URL pattern时,你可以提供应用和实例的命名空间作为include()的参数.例如:

url(r'^polls/', include('polls,urls', namespace='author-polls', app_name='polls')),

This will include the URLs defined in polls.urls into the application namespace ‘polls’, with the instance namespace’author-polls’.
Secondly, you can include an object that contains embedded namespace data. If you include() a list of url() instances, the URLs contained in that object will be added to the global namespace. However, you can also include() a 3-tuple containing:

(<list of url() instances>, <application namespace>, <instance namespace>)

例如:

from django.conf.urls import include, url
from . import views

polls_patterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
]

url(r'^polls/', include((polls_patterns, 'polls', 'author-polls'))),

This will include the nominated URL patterns into the given application and instance namespace.

For example, the Django admin is deployed as instances of AdminSite. AdminSite objects have a urls attribute: A 3-tuple that contains all the patterns in the corresponding admin site, plus the application namespace ‘admin’, and the name of the admin instance. It is this urls attribute that you include() into your projects urlpatterns when you deploy an admin instance.

Be sure to pass a tuple to include(). If you simply pass three arguments: include(polls_patterns, ‘polls’, ‘author-polls’), Django won’t throw an error but due to the signature of include(), ‘polls’ will be the instance namespace and ‘author-polls’ will be the application namespace instead of vice versa.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值