Django第八篇,深入理解视图和url

目录

 URL 配置小技巧

 简化导入函数的方式

 在调试模式下提供特殊的 URL

 具名分组

示例请求:

 捕获的参数始终是字符串

为视图的参数指定默认值

错误处理

 引入其他 URL 配置

捕获的参数

给视图函数传递额外参数

反向解析 URL

示例

URL命名空间


 URL 配置小技巧

 简化导入函数的方式

from mysite.views import hello, current_datetime, hours_ahead

我们可以导入 views 模块自身避免 配置的内容会越变越多的麻烦

from . import views

from mysite.views import *

 在调试模式下提供特殊的 URL

当 DEBUG 的值为 True 时, /debuginfo/ URL 才可以访问

urlpatterns = [
    url(r'^$', views.homepage),
    url(r'^(\d{4})/([a-z]{3})/$', views.archive_month),
]
if settings.DEBUG:
    urlpatterns += [url(r'^debuginfo/$', views.debug),]

 具名分组

在高级用法中,可以使用具名的正则表达式分组捕获 URL 片段,通过关键字参数把片段传给视图。

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^reviews/2003/$', views.special_case_2003),
    url(r'^reviews/([0-9]{4})/$', views.year_archive),
    url(r'^reviews/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^reviews/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.review_detail),
]

• 若想捕获 URL 中的值,把值放在括号里
无需使用前导斜线,每个 URL 本身就有。例如,是 ^reviews ,而非 ^/reviews 。
• 每个正则表达式字符串前面的 'r' 是可选的,但是建议加上。它的作用是告诉 Python,那是“原始”字
符串,里面的一切内容都不应该转义。

示例请求:

• 对 /reviews/2005/03/ 的请求匹配上述列表中的第三个条目。Django 调用 views.month_archive(re-quest, '2005', '03') 函数。
• /reviews/2005/3/ 不匹配任何 URL 模式,因为列表中的第三个条目要求月份有两个数字。
• /reviews/2003/ 匹配列表中的第一个模式,而不是第二个,因为模式按顺序测试,而第一个就能让测试通过。你可以调整顺序,添加类似这样的特例。

下述 URL 配置与前面的作用一样,不过换用具名分组了:

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^reviews/2003/$', views.special_case_2003),
    url(r'^reviews/(?P<year>[0-9]{4})/$',     views.year_archive),
    url(r'^reviews/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$',
views.month_archive),
    url(r'^reviews/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$',
views.review_detail),
]

• 对 /reviews/2005/03/ 的请求调用 views.month_archive(request,year='2005',month='03') 函数,而不是 views.month_archive(request,'2005','03') 。
• 对 /reviews/2003/03/03/ 的请求调用 views.review_detail(re-quest,year='2003',month='03',day='03') 函数。

 捕获的参数始终是字符串

不管正则表达式匹配的是什么类型,捕获的每个参数都以普通的 Python 字符串传给视图。对 URL 配置中的这一行来说:

url(r'^reviews/(?P<year>[0-9]{4})/$', views.year_archive),

虽然 [0-9]{4} 只匹配字符串中的整数,但是传给 views.year_archive() 视图函数的 year 参数是字符串,而不是整数。

为视图的参数指定默认值

# URL 配置
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^reviews/$', views.page),
    url(r'^reviews/page(?P<num>[0-9]+)/$', views.page),
]
# 视图(在 reviews/views.py 文件中)
def page(request, num="1"):
    # 输出指定数量的书评

在上述示例中,两个 URL 模式指向同一个视图,即 views.page ,但是第一个模式没有从 URL 中匹配任何内容。如果匹配第一个模式, page() 函数使用 num 的默认值,即 "1" ;如果匹配第二个模式, page() 函数使用正则表达式匹配的 num 值

 性能

urlpatterns 中的每个正则表达式在首次访问时编译,因此系统的速度异常得快。

错误处理

找不到匹配所请求 URL 的正则表达式或有异常抛出时,Django 会调用一个错误处理视图。具体使用的视图由四个参数指定。这四个参数是:

• handler404
• handler500
• handler403
• handler400

 引入其他 URL 配置

Django 遇到 include() 时,会把截至那一位置匹配的 URL 截断,把余下的字符串传给引入它的 URL 配置,做进一步处理。利用这一行为可以去除 URL 配置中的重复,在多处使用相同的模式前缀。来看这个 URL 配置:

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),
]

我们可以改进这个 URL 配置,把共用的路径前缀提取出来,再把不同的部分放在其后:

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),
        ])    
    ),
]

捕获的参数

被引入的 URL 配置会接收到父级 URL 配置捕获的参数,因此下述示例是有效的:

# settings/urls/main.py
from django.conf.urls import include, url
urlpatterns = [
    url(r'^(?P<username>\w+)/reviews/', include('foo.urls.reviews')),
]
# foo/urls/reviews.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.reviews.index),
    url(r'^archive/$', views.reviews.archive),
]

给视图函数传递额外参数

URL 配置允许向视图函数传递额外的参数,这些参数放在一个 Python 字典中。

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

对这个示例来说,请求 /reviews/2005/ 时,Django 调用 views.year_archive(request, year='2005',foo='bar') 。

反向解析 URL

在 Django 项目中经常需要获取 URL 的最终形式,这么做是为了在生成的内容中嵌入 URL(视图和静态资源的 URL、呈现给用户的 URL,等等),或者在服务器端处理导航流程(重定向等)。此时,一定不能硬编码 URL(费时、不可伸缩,而且容易出错),或者参照 URL 配置创造一种生成 URL 的机制,因为这样容易导致线上 URL 失效。也就是说,我们需要一种不自我重复的机制。

这种机制的一个优点是,改进 URL 设计之后无需在项目的源码中大范围搜索,替换掉过期的 URL。目前,我们能获取的信息有负责处理 URL 的视图标识(即视图名称),以及查找正确 URL 所需的视图参数类型(位置参数或关键字参数)和值。

Django 提供了一种方案,只需在 URL 映射中设计 URL。我们为其提供 URL 配置,然后可以双向使用:
• 从用户(浏览器)请求的 URL 开始,这个方案能调用正确的 Django 视图,并从 URL 中提取可能需要的参数及其值,传给视图。
• 从 Django 视图对应的标识以及可能传入的参数值开始,获取相应的 URL。

Django 在不同的层中提供了执行 URL 反转所需的工具:

• 在模板中,使用 url 模板标签。
• 在 Python 代码中,使用 django.core.urlresolvers.reverse() 函数。

• 在处理 Django 模型实例 URL 相关的高层代码中,使用 get_absolute_url() 方法。

示例

仍以下述 URL 配置为例:

from django.conf.urls import url
from.import views
urlpatterns = [
    #...
    url(r'^reviews/([0-9]{4})/$', views.year_archive,
name='reviews-year-archive'),
    #...
]

根据这个设计,nnnn 年的存档对应的 URL 是 /reviews/nnnn/ 。在模板中可以使用下述代码获取这个 URL:

<a href="{% url 'reviews-year-archive' 2012 %}">2012 Archive</a>
{# 或者把年份存储在一个模板上下文变量中:#}
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'reviews-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 = 2012
    # ...
    return HttpResponseRedirect(reverse('reviews-year-archive', args=(year,)))

URL命名空间

URL 命名空间在反转具名 URL 模式时具有唯一确定性,即便不同的应用使用相同的名称也不怕。鉴于此,第三方应用最好始终把 URL 放在命名空间中。类似地,URL 命名空间在部署多个应用程序实例时也能正确反转 URL。也就是说,同一个应用程序的多个实例使用相同的 URL 模式名称,而通过命名空间可以把它们区分开。

例如:

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

上述代码把 reviews.urls 中定义的 URL 放在应用命名空间 reviews 中,放在实例命名空间 author-reviews中。第二种方式是,引入包含命名空间数据的对象。如果使用 include() 引入一组 url() 实例,那个对象中的 URL 都添加到全局命名空间中。然而, include() 的参数还可以是一个三元素元组,其内容如下:

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

例如:

from django.conf.urls import include, url
from . import views
reviews_patterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
]
    url(r'^reviews/', include((reviews_patterns, 'reviews', 'author-reviews'))),

上述代码把指定的 URL 模式引入指定的应用和实例命名空间中。例如,部署的 Django 管理后台是 AdminSite的实例。 AdminSite 对象有个 urls 属性,它的值是一个三元素元组,包含相应管理后台的所有 URL 模式,以及应用命名空间 'admin' 和管理后台实例的名称。部署管理后台实例时,引入项目的 urlpatterns 中的就是这个 urls 属性

记得要把一个元组传给 include() 。如果直接传入三个参数,例如 include(reviews_patterns, 'reviews','author-reviews') ,Django 不会抛出错误,但是根据 include() 的签名, 'reviews' 是实例命名空间, 'au-thor-reviews' 是应用命名空间,而正确的顺序应该反过来。

猜你喜欢

转载自blog.csdn.net/Da___Vinci/article/details/84521819