2 视图解析顺序_Django URL深入解析

d3267c20649bcedbe3c301c2073a21e3.png

在Understand Django系列的最后一篇文章中,我们看到了用户的浏览器请求是如何从浏览器转到Django的“前门”的,现在是时候看看Django如何处理这些请求了。

来自浏览器的HTTP请求包含一个URL,用来描述Django应该提供哪个资源。由于url可以有多种形式,我们必须告诉Django我们的web应用程序可以处理的url类型。这就是URL配置的目的。在Django文档中,URL配置简称为URLconf。

URLconf在哪里?URLconf位于项目设配置文件中的ROOT_URLCONF所设置的模块路径。如果运行startproject命令,则该设置将命名为类似project.url,其中“project”是命令中参数给定的名称。换句话说,URLconf就放在project/urls.py中的settings.py文件旁边。

这说明了文件所在的位置,但并没有告诉我们它是如何工作的。让我们再深入一些。

URLconf实战

尝试将URL配置看作是Django提供的从上到下匹配的URL路径列表。当Django找到匹配的路径时,HTTP请求将路由到与该路径关联的Python代码块。这个“Python代码块”被称为视图,我们将在稍后对其进行更多的探讨。目前,请相信视图知道如何处理HTTP请求。

我们可以使用一个URLconf示例来展示这一点。

53c7fdc31f586c967211ca415554e3be.png

这里的内容与我上面描述相匹配:Django将尝试从上到下匹配URL路径列表。此列表的关键点是urlpatterns。Django将把urlpatterns变量列表视为URLconf。

名单上的顺序也很重要。该示例没有显示路径之间的任何冲突,但是可以创建两个不同的路径,它们可以匹配用户提交的同一个URL。在我们看到路径的另一个方面之后,我将举一个这样的例子。

我们可以通过这个例子来了解www.acme.com的工作原理。考虑URLconf中的URL时,Django不使用(https://)、域(www.acme.com)和前导斜杠进行匹配。其他一切都是URLconf将要匹配的。

对https://www.acme.com/about/请求的匹配过程的将看起来像“about/”,并匹配第二个路径。该请求将路由到views.about视图。

对https://www.acme.com/的请求在模式匹配过程中看起来像“”并匹配第一个路径。该请求将路由到views.home视图。

旁白:您可能注意到Django url以斜线字符结尾。事实上,如果您尝试访问像https://www.acme.com/about这样的URL,Django会将请求重定向到附加斜杠的同一URL,因为附加斜杠是默认设置。这种行为是由Django的设计理念决定的。

我们面前的道路

如果我给你的只是上面的例子,然后你认为“哇,Django是个笨蛋。为什么urlpatterns不是下面这样的字典?”,我不会责怪你。

5d7d78eac019b484e599317c4b5f61da.png

path 比我最初揭示的更强大。大部分能力都包含在传递给函数的第一个字符串参数中。路径的字符串部分(例如,“about/”)称为路由。

正如您所看到的,路由是一个简单的字符串,但是它可以包含其他特殊结构和一个称为转换器的特性。使用转换器时,可以从视图的URL中提取信息。想想这样一条路径:

b5939da7f599b9987a9c1a4d7cd796cf.png

此路径中的两个转换器是:

尖括号和一些保留名称会使Django尝试对URL进行额外的解析。每个转换器都有一些预期的规则要遵循。

int转换器必须与整数匹配。Slug转换器必须与Slug匹配。Slug是一种出现在Django的报刊术语,因为Django是从堪萨斯州的一家报刊开始的。slug是一个字符串,可以包含字符、数字、破折号和下划线。

给定了这些转换器定义,让我们与一些url进行比较!

  • https://www.acme.com/blog/2020/urls-lead-way/ - 匹配!

  • https://www.acme.com/blog/twenty-twenty/urls-lead-way/ - 不行.

  • https://www.acme.com/blog/0/life-in-rome/ - 匹配!可能不是我们想的那样,我们等下再解释。

现在我们可以重新审视一下之前的顺序问题。以不同的顺序考虑这两条路径:

520139bf629fae3b528a3e6eacd5db60.png

在第一次中,转换器将匹配blog/之后的任何整数,包括https://www.acme.com/blog/2020/。这意味着第一次排序将永远不会调用blog_for_twenty_twenty视图,因为Django按顺序匹配。

相反,在第二个顺序中,blog/2020/将正确地路由到blog_for_twenty_twenty,因为它首先匹配。这意味着要记住:

当包含与转换器匹配的路径项时,请确保将它们放在更具体的项之后。

简略的观点

转换器如何处理这些额外的数据?这很难在不涉及视图的情况下解释。下一篇文章将更深入地讨论这些观点,但这里有一个入门。

视图是接受请求并返回响应的代码。使用Python的可选类型检查,下面是一个发送Hello World响应的示例。

01efe514ba06016258906d02aa9f6f83.png

HTTP request是Django转换的HTTP请求格式,包装在一个方便的容器类中。同样,我们可以使用HTTP response,以便Django将我们的响应数据转换为格式正确的HTTP响应,并将其发送回用户的浏览器。

现在我们可以再看一个转换器。

9fd8265200f78fb2d5a3442322b2f498.png

使用这个转换器,blog-by-year会是什么样子?

da6a9e5e7da3ea92548fb72d65a494d5.png

Django开始在这里展示一些不错的品质!转换器为我们做了一堆乏味的工作。Django设置的year参数已经是一个整数,因为Django进行了字符串解析和转换。

如果有人提交/blog/not_a_number/,Django将返回not Found响应,因为not_a_number不能转换成整数。这样做的好处是,我们不必在 blog_by_year中添加额外的检查逻辑来处理不合法的输入年份。这种功能可以节省时间!它使代码更干净,处理更精确。

我们之前在/blog/0/life in rome/上看到的另一个奇怪的例子呢?这与前面部分中的模式相匹配,但假设我们希望匹配四位数的年份,我们怎么能做到?我们可以使用正则表达式。

正则表达式路径

正则表达式是一种编程特性,常常被比作电锯:它们功能强大得令人难以置信,但如果不小心的话,你可以砍掉你的脚。

正则表达式可以以非常简洁的方式表示复杂的关系和匹配模式。这种简洁性常常给正则表达式带来难以理解的坏名声。但如果仔细使用,它们可能是一个很好用的工具。

正则表达式(通常缩写为“regex”)适合的一项工作是匹配字符串中的复杂模式。这听起来像是我们blog year的问题!在我们的问题中,我们只想匹配一个四位数的整数。让我们看看Django的解决方案,然后阐释它的含义。

作为提醒,此解决方案将匹配某些URL路径,如blog/2020/URL lead way/。

6c1ab5f442a6a0f6df24dc8efd1a968d.png

这个疯狂的字符串的作用与前面的例子完全一样,只是它更精确地限制了四位数的年份。疯狂的弦也有名字。它被称为regex模式。当Django代码运行时,它将根据此模式中定义的规则测试URL路径。

要了解它是如何工作的,我们必须知道模式的各个部分意味着什么。我们可以一次解释一块。

插入符号,^,意思是“模式必须从这里开始”。因为插入符号,一个像myblog/一样开始的路径。。。不会起作用。

blog/是字面上的解释。这些字符必须完全匹配。

圆括号内的部分(?P[0-9]{4})是一个捕获组。这个?P是与捕获组关联的名称,类似于这样的转换器中的冒号右侧。该名称允许Django将名为year的参数中的内容传递给视图。

捕获组[0-9]{4}的另一部分是模式实际匹配的部分。[0-9]是一个字符类,意思是“匹配从0到9的任意数字,{4}表示它必须精确匹配四次。这是re_path给出的int转换器不能提供的特性!

捕获组之间的斜杠/,是另一个要匹配的文本字符。

第二个捕捉组,(?P[\w-]+),将匹配的内容放入名为slug的参数中。[\w-]的字符类包含两种类型的字符。\w是指自然语言中的任何单词字符。另一种类型的字符是文字破折号,-,字符。最后,+,字符表示字符类必须匹配1次或多次。

最后一个斜杠也是文字字符匹配。

为了完成这个模式,美元符号$的作用与插入符号相反,表示“模式必须在这里结束”。因此,blog/2020/some slug/another slug/将不匹配。

祝贺你!这绝对是本文最难的部分。如果你明白我们对re_path做了什么,剩下的应该会感觉很舒服。如果没有,请不要担心!如果您想了解有关正则表达式的更多信息,请明白我在模式中描述的所有内容都不是特定于Django的。相反,这是Python的内置行为。您可以从Python的正则表达式HOWTO了解更多关于正则表达式的信息。

知道了re_path的力量,即使你今天不需要它,也可以帮助你以后的Django之旅。

为相关的URL分组

到目前为止,我们已经研究了可以在URLconf中映射的各个路由。当相关的视图组应该共享一条公共路径时,我们可以做什么?我们为什么要这么做?

让我们想象一下你正在建设一个教育项目。在您的项目中,有学校、学生和其他与教育相关的概念。你可以这样做:

bad9cd55a8f940569d0b45cdf237e3ef.png

这种方法可以很好地工作,但是它迫使根URLconf了解每个应用程序、学校和学生中定义的所有视图。作为替代,我们可以使用include来更好地处理这个问题。

ddd87f3b221c9822fce0ee41e6dc2353.png

然后,在每个应用程序中,我们都会得到如下内容:

b3059ad3ea72c5ada7d9ea8756da521e.png

include使每个Django应用程序在需要定义的视图中拥有自主权。这个项目可以很幸福地“不知道”应用程序正在做什么。

此外,从第一个例子中删除了schools/或students/的重复。当Django处理一个路由时,它将匹配路由的第一部分,并将其余部分传递到在单个应用程序中定义的URLconf。通过这种方式,URL配置可以形成一个树,根URLconf是所有请求开始的地方,但是当请求路由到适当的应用程序时,各个应用程序可以处理细节。

命名URL

我们已经研究了用path、re-path和include定义url的主要方法。还有另外一个方面需要考虑。我们如何引用代码中其他地方的url?想想这个(相当愚蠢的)视图:

10bd986b998ce70aae0689a9f2c96f43.png

重定向是指用户试图访问一个页面,并被浏览器发送到其他地方。处理重定向的方法比这个示例显示的要好得多,但是这个视图说明了一个不同的观点。如果您希望重新构造项目,以便将博客类别从/blog/categories/to/marketing/blog/categories/移动到/marketing/blog/categories/,会发生什么情况?在当前表单中,我们必须修复此视图和直接引用路由的任何其他视图。

真是浪费时间!Django为我们提供了一些工具来提供独立于显式路由的路径名。我们使用path的name关键字参数来实现这一点。

5291201b9346b733fd09800a83639389.png

这使我们的blog_categories在/marketing/blog/categories/路径中独立命名。要使用这个名称,我们需要reverse 作为其对应项。我们修改后的视图如下:

7060a24408767174672baaf20e2b8f83.png

reverse的任务是查找任何路径名并返回其路由。这意味着:

0af4440627a2859290e4091c78def6e7.png

至少在你想再换一次之前。

当名字冲突时

如果您有多个要给出相同名称的url,会发生什么情况?例如,index或detail是可能要应用的公用名称。我们可以向The Zen of Python 寻求建议。

提姆·彼得斯的《The Zen of Python》

美胜于丑。…命名空间是一个很好的主意-让我们做更多!

如果编程时间不长,命名空间对您来说可能是全新的概念。它们是命名共享的空间。也许这很清楚,但我记得当我第一次开始写软件的时候,我还在为这个概念而挣扎。

为了与现实世界中的事物进行类比,让我们使用可信的桶。假设你有两个红球和两个蓝球。把一个颜色的球放在两个标有“A”和“B”的桶里。如果我想要一个特定的蓝球,我不能说“请把蓝球给我”,因为那样会模棱两可。相反,为了得到一个特定的球,我需要说“请给我bucket B中的蓝色球。”在这个场景中,bucket是命名空间。

我们在学校和学生中使用的示例可以帮助在代码中说明这个想法。两个应用程序都有一个index视图来表示项目各个部分的根(即学校/和学生/)。如果我们想关联到这些视图,我们会尽量选择最简单的index作为名称。不幸的是,如果选择index,那么Django无法判断哪个视图是index的正确视图。名称不明确。

一种解决方案是通过在名称前面加上一些常见的东西(如schools)来创建自己的命名空间。这种方法的问题在于URLconf会自我重复。

7e7cedb08a07a1966c700cea46baa882.png

Django提供了一个选项,可以让您保留一个较短的名称。

27beee44e08b3892e2abb60b409f4b4f.png

通过添加app_name,我们向Django发出信号,这些视图位于一个名称空间中。现在,当我们想要获得一个URL时,我们使用名称空间名称和URL名称,并用冒号将它们连接起来。

c3f63e3c9e6d4890df98c836a5449763.png

这是Django为简化我们的应用程序开发提供的另一个便利。

这就结束了url的话题。现在,我们已经看到了如何:

  • 通过创建包含urlpatterns列表的模块来进行URL配置。

  • 使用path 和re_path创建URL。

  • 使用转换器为视图提取信息。

  • 使用正则表达式来表示更复杂的URL数据。

  • 将相关的url与include一起分组。

  • 按名称引用URL。

  • 将相关名称放在命名空间中。

在下一篇文章中,我们将深入探讨各种观点。这篇文章只对什么是视图给出了最简单的定义。Django在处理视图时为我们提供了非常丰富的选项。我们将探索:

  • 视图函数

  • 视图类

  • 一些内置的视图

  • 视图装饰器。

如果你想继续这个系列,请随时订阅,在那里我宣布我的所有新内容。如果你还有其他问题,可以在Twitter上@mblayman。

英文原文:https://www.mattlayman.com/understand-django/urls-lead-way/ 
译者:QL

613280371478b0e1672ed6c76421af40.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值