Django 处理HTTP请求


本文整理自网络,侵删。

URL调度

干净,优雅的URL方案是高质量Web应用程序中的重要细节。Django允许您根据需要设计URL,而无框架限制。

万维网创建者蒂姆·伯纳斯-李(Tim Berners-Lee)的文章“ Cool URIs not not change”中有关为什么URL应该干净和可用的出色论据,请参见。

概述

要设计应用程序的URL,您可以创建一个非正式地称为URLconf(URL配置)的Python模块 。该模块是纯Python代码,并且是URL路径表达式到Python函数(您的视图)之间的映射。

该映射可以根据需要短或长。它可以引用其他映射。而且,由于它是纯Python代码,因此可以动态构建。

Django还提供了一种根据活动语言翻译URL的方法。有关更多信息,请参见国际化文档。

Django是如何处理一个请求

当用户从您的Django支持的网站请求页面时,系统将使用以下算法来确定要执行的Python代码:

  1. Django确定要使用的根URLconf模块。通常,这是ROOT_URLCONF设置的值,但是如果传入 HttpRequest对象具有urlconf 属性(由中间件设置),则将使用其值代替 ROOT_URLCONF设置。
  2. Django加载该Python模块并查找变量 urlpatterns。这应该是一个序列的 django.urls.path()和/或django.urls.re_path()实例。
  3. Django按顺序遍历每个URL模式,并在第一个与请求的URL匹配的URL处停止,与匹配 path_info。
  4. 一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,该视图是Python函数(或基于类的视图)。该视图将传递以下参数:的实例HttpRequest。如果匹配的URL模式不包含命名组,则来自正则表达式的匹配项将作为位置参数提供。关键字参数由提供的路径表达式匹配的任何命名部分组成,这些名称部分由或 的可选kwargs参数中指定的任何参数覆盖。django.urls.path()django.urls.re_path()在Django 3.0中进行了更改:在旧版本中,带有None值的关键字参数也由未提供的命名部分组成。
  5. 如果没有URL模式匹配,或者在此过程中的任何时候引发异常,Django都会调用一个适当的错误处理视图。请参阅下面的错误处理。

例子

这是一个示例URLconf:

from django.urls import path
?
from . import views
?
urlpatterns = [
  path('articles/2003/', views.special_case_2003),
  path('articles/<int:year>/', views.year_archive),
  path('articles/<int:year>/<int:month>/', views.month_archive),
  path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]

笔记:

  • 要从URL捕获值,请使用尖括号。
  • 捕获的值可以选择包括转换器类型。例如,用于 `捕获整数参数。如果不包括转换器/`,则匹配除字符以外的任何字符串。
  • 无需添加斜杠,因为每个URL都有该斜杠。例如articles,不是/articles。

请求示例:

  • 请求/articles/2005/03/匹配列表中的第三个条目。Django将调用该函数 。views.month_archive(request, year=2005, month=3)
  • /articles/2003/会匹配列表中的第一个模式,而不是第二个,因为这些模式是按顺序测试的,而第一个是第一个通过的测试。随意利用命令来插入类似这样的特殊情况。在这里,Django将调用该函数 views.special_case_2003(request)
  • /articles/2003 不会与任何这些模式匹配,因为每种模式都要求URL以斜杠结尾。
  • /articles/2003/03/building-a-django-site/将匹配最终模式。Django将调用该函数 。views.article_detail(request, year=2003, month=3, slug="building-a-django-site")

路径转换器

默认情况下,以下路径转换器可用:

  • str-匹配任何非空字符串,但路径分隔符除外'/'。如果表达式中不包含转换器,则为默认设置。
  • int-匹配零或任何正整数。返回int。
  • slug-匹配由ASCII字母或数字以及连字符和下划线字符组成的任何条形字符串。例如, building-your-1st-django-site。
  • uuid-匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。
  • path-匹配任何非空字符串,包括路径分隔符 '/'。这样,您就可以匹配完整的URL路径,而不是像一样匹配URL路径的一部分str。

注册自定义路径转换器

对于更复杂的匹配要求,您可以定义自己的路径转换器。

转换器是包含以下内容的类:

  • 一regex类属性,作为一个字符串。
  • 一种方法,用于将匹配的字符串转换为应传递给视图函数的类型。如果无法转换给定值,则应加注。A 被解释为不匹配,结果404响应会发送给用户,除非另一个URL模式匹配。to_python(self, value)``ValueError``ValueError
  • 一种方法,用于将Python类型转换为要在URL中使用的字符串。to_url(self, value)

例如:

class FourDigitYearConverter:
  regex = '[0-9]{4}'
?
  def to_python(self, value):
      return int(value)
?
  def to_url(self, value):
      return '%04d' % value

使用register_converter()以下命令在URLconf中注册自定义转换器类 :

from django.urls import path, register_converter
?
from . import converters, views
?
register_converter(converters.FourDigitYearConverter, 'yyyy')
?
urlpatterns = [
  path('articles/2003/', views.special_case_2003),
  path('articles/<yyyy:year>/', views.year_archive),
  ...
]

使用正则表达式

如果路径和转换器语法不足以定义URL模式,则还可以使用正则表达式。为此,请使用 re_path()代替path()

在Python正则表达式中,命名正则表达式组的语法为(?Ppattern),其中name是组的名称,并且 pattern是匹配的某种模式。

这是前面的示例URLconf,使用正则表达式重写:

from django.urls import path, re_path
?
from . import views
?
urlpatterns = [
  path('articles/2003/', views.special_case_2003),
  re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
  re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
  re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
]

这可以完成与上一个示例大致相同的操作,除了:

  • 将要匹配的确切URL受到更多限制。例如,年份10000将不再匹配,因为年份整数被限制为正好是四位数长。
  • 无论正则表达式进行哪种匹配,每个捕获的参数都将作为字符串发送到视图。

当从使用切换为使用path(), re_path()反之亦然时,特别重要的是要注意视图参数的类型可能会更改,因此您可能需要调整视图。

使用未命名的正则表达式组

除了命名组语法(例如)之外(?P[0-9]{4}),您还可以使用较短的未命名组(例如)([0-9]{4})。

不建议特别使用此用法,因为这样可以更轻松地在匹配的预期含义和视图的参数之间意外引入错误。

无论哪种情况,建议在给定的正则表达式中仅使用一种样式。当两种样式混合使用时,任何未命名的组都会被忽略,只有命名的组才会传递给视图函数。

嵌套参数

正则表达式允许嵌套参数,而Django会解析它们并将其传递给视图。反转时,Django将尝试填写所有外部捕获的参数,而忽略任何嵌套的捕获参数。考虑以下URL模式,这些URL模式可以选择采用page参数:

from django.urls import re_path
?
urlpatterns = [
  re_path(r'^blog/(page-(\d+)/)?$', blog_articles),                 # bad
  re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments), # good
]

两种模式都使用嵌套参数,并将解析:例如, blog/page-2/将导致与匹配blog_articles两个位置参数:page-2/和2。的第二个模式 comments将comments/page-2/与关键字参数 page_number设置为2 匹配。在这种情况下,外部参数是一个非捕获参数(?:...)。

该blog_articles视图需要最外层捕获的参数被反转,page-2/或者在这种情况下不需要参数,而视图 comments可以不带参数也没有值而被反转page_number。

嵌套的捕获参数在视图参数和URL之间建立了牢固的耦合,如下所示blog_articles:视图接收部分URL(page-2/),而不是仅接收视图感兴趣的值。这种反转在反转时更为明显,因为反转视图,我们需要传递该URL而不是页码。

根据经验,当正则表达式需要参数但视图将其忽略时,仅捕获视图需要使用的值,并使用非捕获参数。

URLconf搜索的内容

URLconf按照正常的Python字符串搜索请求的URL。这不包括GET或POST参数或域名。

例如,在对的请求中https://www.example.com/myapp/,URLconf将寻找myapp/。

在请求中https://www.example.com/myapp/?page=3,URLconf将寻找myapp/。

URLconf不会查看请求方法。换句话说,所有的请求方法- ,,POST 等-将被路由到相同的URL相同的功能。GET``HEAD

为视图参数指定默认值

一个方便的技巧是为视图的参数指定默认参数。这是一个示例URLconf和视图:

# URLconf
from django.urls import path
?
from . import views
?
urlpatterns = [
  path('blog/', views.page),
  path('blog/page<int:num>/', views.page),
]
?
# View (in blog/views.py)
def page(request, num=1):
  # Output the appropriate page of blog entries, according to num.
  ...

在上面的示例中,两个URL模式都指向同一视图– views.page–但是第一个模式未从URL中捕获任何内容。如果第一个模式匹配,该page()函数将使用它的默认参数num,1。如果第二个模式匹配, page()将使用num捕获的任何值。

性能

中的每个正则表达式urlpatterns都是在首次访问时进行编译。这使系统运行起来非常快。

在语法urlpatterns变量

urlpatterns应该是一个序列的path() 和/或re_path()实例。

错误处理

当Django无法找到所请求URL的匹配项或引发异常时,Django会调用错误处理视图。

这些情况下使用的视图由四个变量指定。它们的默认值足以满足大多数项目的需要,但可以通过覆盖其默认值来进行进一步的自定义。

有关完整的详细信息,请参见有关自定义错误视图的文档。

可以在您的根URLconf中设置这些值。在任何其他URLconf中设置这些变量将无效。

值必须是可调用的,或者是表示视图的完整Python导入路径的字符串,应该调用该视图来处理当前的错误情况。

变量是:

  • handler400–请参阅django.conf.urls.handler400。
  • handler403–请参阅django.conf.urls.handler403。
  • handler404–请参阅django.conf.urls.handler404。
  • handler500–请参阅django.conf.urls.handler500。

包括其他的URLconf 

在任何时候,您urlpatterns都可以“包括”其他URLconf模块。实质上,这会将“ URL”“植根”在其他URL之下。

例如,这是Django网站 本身的URLconf的摘录。它包括许多其他URLconf:

from django.urls import include, path
?
urlpatterns = [
  # ... snip ...
  path('community/', include('aggregator.urls')),
  path('contact/', include('contact.urls')),
  # ... snip ...
]

每当Django遇到时include(),它都会截断直到该时间点匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf中以进行进一步处理。

另一种可能性是通过使用path()实例列表包括其他URL模式 。例如,考虑以下URLconf:

from django.urls import include, path
?
from apps.main import views as main_views
from credit import views as credit_views
?
extra_patterns = [
  path('reports/', credit_views.report),
  path('reports/<int:id>/', credit_views.report),
  path('charge/', credit_views.charge),
]
?
urlpatterns = [
  path('', main_views.homepage),
  path('help/', include('apps.help.urls')),
  path('credit/', include(extra_patterns)),
]

在此示例中,/credit/reports/URL将由credit_views.report()Django视图处理 。

阅读剩余部分

相关阅读 >>

使用基于类的视图进行表单处理

内置基于类的通用视图

Django 教程介绍

基于类的视图简介

Django 模型

Django的缓存框架

使用wsgi进行部署

Django 使用表单模板

部署清单

错误报告

更多相关阅读请进入《Django》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...