URL路由基础

阅读: 8154


URL是Web服务的入口,用户通过浏览器发送过来的任何请求,都是发送到一个指定的URL地址,然后被响应。

在Django项目中编写路由,就是向外暴露我们接收哪些URL的请求,除此之外的任何URL都不被处理,也没有返回。通俗地理解,不恰当的形容,URL路由是你的Web服务对外暴露的API。

Django奉行DRY主义,提倡使用简洁、优雅的URL,没有.php.cgi这种后缀,更不会单独使用0、2097、1-1-1928、00这样无意义的东西,让你随心所欲设计你的URL,不受框架束缚。

一、概述

URL路由在Django项目中的体现就是urls.py文件,这个文件可以有很多个,但绝对不会在同一目录下。实际上Django提倡项目有个根urls.py,各app下分别有自己的一个urls.py,既集中又分治,是一种解耦的模式。

随便新建一个Django项目,默认会自动为我们创建一个/project_name/urls.py文件,并且自动包含下面的内容,这就是项目的根URL:

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

前面一堆帮助性的文字,我们不用管,关键是默认导入了url和admin,然后有一条指向admin后台的url路径。

我们自己要编写的url路由,基本也是这个套路。

二、Django如何处理请求

当用户请求一个页面时,Django根据下面的逻辑执行操作:

  1. 决定要使用的根URLconf模块。通常,这是ROOT_URLCONF设置的值,但是如果传入的HttpRequest对象具有urlconf属性(由中间件设置),则其值将被用于代替ROOT_URLCONF设置。通俗的讲,就是你可以自定义项目入口url是哪个文件!
  2. 加载该模块并寻找可用的urlpatterns。 它是django.conf.urls.url()实例的一个列表。
  3. 依次匹配每个URL模式,在与请求的URL相匹配的第一个模式停下来。也就是说,url匹配是从上往下的短路操作,所以url在列表中的位置非常关键。
  4. 导入并调用匹配行中给定的视图,该视图是一个简单的Python函数(被称为视图函数),或基于类的视图。 视图将获得如下参数:
    1. 一个HttpRequest 实例。
    2. 如果匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将作为位置参数提供给视图。
    3. 关键字参数由正则表达式匹配的命名组组成,但是可以被django.conf.urls.url()的可选参数kwargs覆盖。
  5. 如果没有匹配到正则表达式,或者过程中抛出异常,将调用一个适当的错误处理视图。

三、简单示例

下面是一个简单的 URLconf:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

我们要编写的就是上面urlpatterns列表中的一条条url,每条url,都是urlpatterns列表的一个元素。先后顺序有重要关系,不能随意摆放。最后一条的末尾建议添加一个逗号。

urlpatterns中的每条正则表达式在第一次访问时被自动编译,因此其匹配速度是非常快的。

注意:

  • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号。
  • 不需要添加前导的反斜杠,因为每个URL都有。 例如,应该是^articles而不是^/articles
  • 每个正则表达式前面的'r'是可选的但是建议加上。它告诉Python这个字符串是“原始的” —— 字符串中任何字符都不应该转义。

根据上面的urlconf,下面是一些请求的例子,以及它们将匹配到的url:

  • /articles/2005/03/将匹配列表中的第三个模式。Django将调用函数views.month_archive(request, '2005', '03')
  • /articles/2005/3/不匹配任何URL模式,因为列表中的第三个模式要求月份是两个数字。
  • /articles/2003/将匹配列表中的第一个模式不是第二个,因为模式按顺序从上往下匹配,第一个会首先被匹配。Django会调用函数views.special_case_2003(request)
  • /articles/2003不匹配任何一个模式,因为每个模式都要求URL以一个斜杠结尾。
  • /articles/2003/03/03/将匹配最后一个模式。Django将调用函数views.article_detail(request, '2003', '03', '03')

四、命名组

很多时候,我们需要获取URL中的一些片段,作为参数,传递给处理请求的视图。

上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL中的值并以位置参数的形式传递给视图。

可以使用命名的正则表达式组来捕获URL中的值并以关键字参数传递给视图。

在Python的正则表达式中,命名组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。

下面是以上URLconf使用命名组的重写:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。 像这样:

  • /articles/2005/03/请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')
  • /articles/2003/03/03/请求将调用函数views.article_detail(request, year='2003', month='03', day='03')

在实际应用中,这让你的URLconf更加明晰且不容易产生参数顺序问题的错误。当然,这不是强制性的,也牺牲了一些简洁性。

针对命名组和非命名组:

  • 如果有命名参数,则使用这些命名参数,忽略非命名参数。
  • 否则,它将以位置参数传递所有的非命名参数。

五、URLconf匹配请求URL中的哪些部分

请求的URL被看做是一个普通的Python字符串,URLconf在其上查找并匹配。进行匹配时将不包括GET或POST请求方式的参数以及域名。

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

https://www.example.com/myapp/?page=3的请求中,URLconf也将查找myapp/

URLconf不检查使用何种HTTP请求方法,所有请求方法POST、GET、HEAD等都将路由到同一个URL的同一个视图。在视图中,才根据具体请求方法的不同,进行不同的处理。

六、URL中捕获的参数为字符串类型

每个捕获的参数都作为一个普通的Python字符串传递给视图,即便被捕获的‘100’看起来像个整数,但实际上是个字符串‘100’。 例如,下面这行URLconf中:

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

传递给views.year_archive()的year参数将是一个字符串,不是整数,即使[0-9]{4}只匹配整数字符串。

七、指定视图参数的默认值

有一个小技巧,我们可以指定视图参数的默认值。 下面是一个URLconf和视图的示例:

# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', 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值。

八、自定义错误页面

当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们分别位于:

  • handler400 —— django.conf.urls.handler400。
  • handler403 —— django.conf.urls.handler403。
  • handler404 —— django.conf.urls.handler404。
  • handler500 —— django.conf.urls.handler500。

这些值可以在根URLconf中设置。在其它app中的二级URLconf中设置这些变量无效。

Django有内置的HTML模版,用于返回错误页面给用户,但是这些403,404页面实在丑陋,通常我们都自定义错误页面。

首先,在根URLconf中额外增加下面的条目:

# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# 增加的条目
handler400 = views.bad_request
handler403 = views.permission_denied
handler404 = views.page_not_found
handler500 = views.page_error

然后在,views.py文件中增加四个处理视图:

def page_not_found(request):
    return render(request, '404.html')


def page_error(request):
    return render(request, '500.html')


def permission_denied(request):
    return render(request, '403.html')


def bad_request(request):
    return render(request, '400.html')

再根据自己的需求,创建404.html、400.html等四个页面文件,就可以了。



评论总数: 5



user_image
在django2.0中, URL中捕获的参数类型

在django2.0中,URL中捕获的参数为字符串类型, 不全部是字符串了. 刘大大,注释一下哈~

user_image
求助

为何 url(r'^blog/page(?P<num>[0-9]+)/$' 我按这样规则写 在视图函数中,def page(request, num="1"),num接收不到数据 如果不给num赋初值,还会报错说没有给num赋值 请问该如何解决

user_image
检查下代码

信息太少,无法准确回答。但从字面上看,url路由没写全,后面关联的是哪个视图函数呢?是没贴出来还是真没写?是不是views.page?

user_image
handler403 = views.bad_request

博主,这个地方应该是400吧?

user_image
错误处理的写法选择

handler404 = views.page_not_found这种是请求不成功,系统自动返回一个错误页面。 question = get_object_or_404(Question, pk=question_id)这种是手动处理转到错误页面。 如果视图类比较多,推荐用第一种写法?