Django—路由(全面)

一个完整的路由包含:路由地址、视图函数(或者视图类)、可选变量和路由命名。

1、路由定义规则

路由称为URL,是对可以从互联网上得到的资源位置和访问方法的一种简洁的表示,是互联网标准资源的地址。互联网上的每个文件都有一个唯一的路由,用于指出网络文件的路径位置。简单说,路由可视为我们常说的网址,每个网址代表不同的网页。

1.1 Django2.X路由定义

这种路由设计模式下,工作原理如下:

  1. 运行MyDjango项目时,Django从MyDjango文件夹的urls.py找到各个App所定义的路由信息,生成完整的路由列表。
  2. 当用户在浏览器上访问某个路由地址时,Django就会收到该用户的请求信息。
  3. Django从当前请求信息获取路由地址,并在路由列表里匹配相应的路由信息,再执行路由信息所指向的视图函数(或视图类),从而完成整个请求响应过程。

MyDjango文件夹的urls.py代码如下:

from django.contrib import admin
from django.urls import path,include
urlpatterns = [
    # 指向内置Admin后台系统的路由文件sites.py
    path('admin/',admin.site.urls),
    # 指向index的路由文件urls.py
    path('',include('index.urls')),
]

MyDjango文件夹的urls.py定义两条路由信息,分别是Admin站点管理和首页地址(index)。其中,Admin站点管理在创建项目时已自动生成,一般情况下无需修改;首页地址是指index文件夹的urls.py。MyDjango文件夹的urls.py的代码解释如下:

  • from django.contrib import admin:导入内置Admin功能模块。
  • from django.urls import path,include:导入Django的路由函数模块。
  • urlpatterns:代表整个项目的路由集合,以列表格式表示,每个元素代表一条路由信息。
  • path(‘admin/’,admin.site.urls):设定Admin的路由信息。'admin/'代表127.0.0.1:8000/admin的路由地址,admin后面的斜杠是路径分隔符,其作用等同于计算机中文件目录的斜杆符号;admin.site.urls指向内置Admin功能所自定义的路由信息,可以在Python目录Lib\site-packages\django\contrib\admin\sites.py找到具体定义过程。
  • path(’’,include(‘index.urls’)):路由地址为“\”,即127.0.0.1:8000,,通常是网站的首页;路由函数include是将该路由信息分发给index的urls.py处理。这里的index的urls.py来源在前面博客中讲到了。

由于首页地址分发给index的urls.py处理,因此下一步需要对index的urls.py编写路由信息。如下:

# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
	path('',views.index)
]

index的urls.py的编写规则与MyDjango文件夹的urls.py大致相同,这是最为简单的定义方法,此外可参考内置Admin功能的路由定义方法。
在index的urls.py导入index的views.py文件,该文件用于编写视图函数或视图类,主要用于处理当前请求信息并返回响应内容给用户。路由信息path(’’,views.index)的views.index是指视图函数index处理网站首页的用户请求和响应过程。因此,在index的views.py中编写index函数的处理过程,代码如下:

from django.shortcuts import render
def index(request):
    return render(request,'index.html')

index函数必须设置一个参数,参数命名不固定,但常以request进行命名,代表当前用户的请求对象,该对象包含当前请求的用户名、请求内容和请求方式等。
视图函数执行完成后必须使用return将处理结果返回,否则程序会抛出异常信息。启动MyDjango项目,在浏览器里访问即可。
路由文件urls.py的路由定义规则是相对固定的,路由列表由urlpatterns表示,每个列表元素代表一条路由。路由由Django的path函数定义的,该函数第一个参数是路由地址,第二个参数是路由所对应的处理函数(视图函数或视图类),这两个参数是路由定义的必选参数。

1.2 路由变量的设置

在日常开发过程中,有时一个路由代表多个不同的页面,如编写带有日期的路由,若根据前面的编写方式,按一年计算,则需要开发者编写365个不同的路由才能实现,实现这种做法明显是不可取的。因此,Django在定义路由时,可以对路由设置变量值,使路由具有多样性。
路由的变量类型有字符类型、整型、slug和uuid,最为常用的使字符类型和整型。各个类型说明如下:

  • 字符类型: 匹配任何非空字符串,但不含斜杆,如果没有指定类型,就默认使用该类型。
  • 整型: 匹配0和正整数。
  • slug: 可理解为注释、后缀或附属等概念,常作为路由的解释性字符。可匹配ASCII字符以及连接符和下画线,能使路由更加清晰易懂。比如网页的标题是"14岁的小娃娃",其路由地址可以设置为“14-sui-de-xiaowawa”。
  • uuid: 匹配一个uuid格式的对象。为了防止冲突,规定必须使用“-”并且所有字母必须小写,例如:075446e5-4646-fajkl46468

根据上述变量类型,在MyDjango项目的index文件夹的urls.py里新定义路由,并且带有字符类型、整型和slug的变量,代码如下:

# index的urls.py文件
from django.urls.import.path
from . import views
urlpatterns = [
	# 添加带有字符类型、整型和slug的路由
	path('<year>/<iny:month>/<slug:day>',views.myvariable)
]

在路由中,使用变量符号“<>”可以为路由设置变量。在括号里面以冒号划分为两部分,冒号前面代表的是变量的数据类型,冒号后面代表的是变量名,变量名可自行命名,如果没有设置变量的数据类型,就默认为字符类型。上述代码设置了3个变量,分别是<year>、<int:month>和<slug:day>,变量说明如下:

  • <year>:变量名为year,数据格式为字符类型,与<str:year>的含义一样。
  • <int:month>:变量名为month,数据格式为整型。
  • <slug:day>:变量名为day,数据格式为slug。

在上述新增的路由中,路由的处理函数为myvariable,因此在index的views.py中编写myvariable的处理过程,代码如下:

# views.py的myvariable函数
from django.http import HttpResponse
def myvariable(request,year,month,day):
	return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))

启动项目,在浏览器上输入127.0.0.1:8000/2020/04/19,运行结果则输出2020/4/19。如果浏览器输入的路由地址与其变量类型不相符合,Django就会提示Page not found。路由的变量和视图函数的参数要一一对应,如果视图函数的参数与路由的变量对不上,那么程序会抛出不相符的报错信息。
除了在路由地址设置变量外,Django还支持在路由地址外设置变量(路由的可选变量)。在index的urls.py和views.py中分别新增路由和视图函数。代码如下:

# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
	# 添加带有字符类型、整型和slug的路由
	path('<year>/<iny:month>/<slug:day>',views.myvariable),
	# 添加路由地址外的变量month
	path('',views.index,{'month':'2019/10/10'})
]

# index的views.py
from django.http import HttpResponse
def myvariable(request,year,month,day):
	return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
	
def index(request,month):
	return HttpResponse('这是路由地址之外的变量:'+month)

从上述代码可以看出,路由函数path的第3个参数是{‘month’:‘2019/10/10’},该参数的设置则如下:

  • 参数只能以字典的形式表示。
  • 设置的参数只能在视图函数中读取和使用。
  • 字典的一个键值对代表一个参数,键值对的键代表参数名,键值对的值代表参数值。
  • 参数值没有数据格式限制,可以为某个实例对象,字符串或列表(元组)等。

视图函数index的参数必须对应字典的键,如果字典里设置两对键值对,视图函数就要设置相应的函数参数,否则在浏览器上访问的时候就会提示报错信息。

1.3 正则表达式的路由定义

前面设置路由地址分别代表日期的年、月、日,其变量类型分别是字符类型、整型和sulg,因此在浏览器上输入127.0.0.1/AAAA/05/01是合法的,但是不符合日期格式要求。为了进一步规范日期格式,可以使用正则表达式限制路由地址变量的取值范围。在index文件夹的urls.py里使用正则表达式定义路由地址,代码如下:

from django.urls import re_path
from . import views
urlpatterns = [
    re_path('(?P<year>[0-9]{4})/(?P<month>[0-9]{2}/(?P<day>[0-9]{2})).html',views.mydate)
]

路由的正则表达式是由路由函数re_path定义的,其作用是对路由变量进行截取与判断,正则表达式是以小括号为单位的,每个小括号的前后可以使用斜杆或者其他字符将其分隔与结束。以上代码为例,分别将变量year、month和day以斜杆隔开,每个变量以一个小括号为单位,在小括号内,可分为3部分,以(?P<year>[0-9]{4})为例。

  • ?P是固定格式,字母P必须为大写。
  • <year>为变量名
  • [0-9]{4}是正则表达式的匹配模式,代表变量的长度为4,只允许取0~9的值。

上述路由的处理函数mydate函数,因此还需要在index的views.py中编写视图函数mydate,代码如下:

# views.py的mydate函数
from django.http import HttpResponse
def mydate(request,year,month,day):
    return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))

路由地址末端设置了".html",这是一种伪静态URL技术,可将网址设置为静态网址,用于SEO搜索引擎的爬取,如百度、谷歌等。此外,在末端设置".html"是为变量day设置终止符,假如末端没有设置".html",在浏览器上输入无限长的字符串,路由也能正常访问。

2、命令空间与路由命名

2.1 命名空间namespace

在MyDjango项目中创建新的项目应用user,并且在user文件里创建urls.py,然后配置文件settings.py的INSTALLED_APPS中添加项目应用user,使得Django在运行的时候能够识别项目应用user。在MyDjango文件夹的urls.py中重新定义路由信息,分别指向index文件夹的urls.py和user文件夹的urls.py,代码如下:

from django.contrib import admin
from django.urls import path,include
urlpatterns = [
    # 指向内置Admin后台系统的路由文件sites.py
    path('admin/',admin.site.urls),
    # 指向index的路由文件urls.py
    path('',include(('index.urls','index'),namespace = 'index')),
    # 指向user的路由文件urls.py
    path('user/', include(('user.urls', 'user'), namespace='user')),
]

上述代码中,新增的路由使用Django路由函数include并且指向index文件夹的urls.py和user文件夹的urls.py。在函数include里设置了可选参数namespace,该参数是函数include特有的参数,这就是Django设置路由的命名空间。
路由函数include设有参数arg和namespace,参数arg指向项目应用App的urls.py文件,其数据格式以元组或字符串表示;可选参数namespace是路由的命名空间。
若要对路由设置参数namespace,则参数arg必须以元组格式表示,并且元组的长度必须为2.元组的元素说明如下:

  • 第一个元素为项目应用的urls.py文件,比如(“index.urls”,“index”)的"index.urls",这是代表项目应用index的urls.py文件。
  • 第二个元素可以自行命名,但不能为空,一般情况下是以项目应用的名称进行命名,(“index.urls”,“index”)的"index"是以项目应用index进行命名的。

如果路由设置参数namespace并且参数arg为字符串或元组长度不足2的时候,在运行MyDjango的时候,Django就会提示错误信息。
接下来分析路由函数include的作用,它是将当前路由分配到某个项目应用的urls.py文件,而项目应用的urls.py文件可以设置多条路由,这种情况类似计算机上的文件夹A,并且该文件夹下包含多个子文件夹,而Django的命名空间namespace相当于对文件夹A进行命名。Django的命名空间namespace可以为我们快速定位某个项目的urls.py,再结合路由命名name就能快速地从项目应用地urls.py找到某条路由地具体信息,这样就能有效管理整个项目的路由列表。有关路由函数include的定义过程,可以在Python安装目录下找到源码(Lib\site-packages\django\urls\conf.py)进行解读。

2.2 路由命名name

上面都使用路由函数include并且分别指向index文件夹的urls.py和user文件夹的urls.py,命名空间namespace分别为index和user。在此基础上,我们在index文件夹的urls.py和user文件夹的urls.py中重新定义路由,代码如下:

# index文件夹的urls.py
from django.urls import re_path,path
from . import views
urlpatterns = [
	re_path('(?P<year>[0-9]{4}.html)',views.mydate,name='mydate'),
	path('',views.index,name = 'index')
]

# user文件夹的urls.py
from django.urls import path
from . import views
urlpatterns =[
	path('index',views.index,name = 'index'),
	path('login',views.userLogin,name = 'userLogin')
]

每个项目应用的urls.py都定义了两条路由,每条路由都由相应的视图函数进行处理,因此在index文件夹的views.py和user文件夹的views.py中定义视图函数,代码如下:

# index文件夹的views.py
from django.http import HttpResponse
from django.shortcuts import render
def mydate(request,year):
	return HttpResponse(str(year))
def index(request):
	return render(request,'index.html')

# user文件夹的views.py
from django.http import HttpResponse
def index(request):
	return HttpResponse("This is userIndex")
def userLogin(request):
	return HttpResponse('This is userLogin')

项目应用index和user的urls.py所定义的路由都设置了参数name,这是对路由进行命名,它是路由函数path和re_path的可选参数。路由命名name的作用等同于文件夹里的文件名。
如果路由里使用路由函数include,就可以对该路由设置参数name,因为路由的命名空间namespace是路由函数include的可选参数,而路由命名name是路由函数path或re_path的可选参数,两者隶属于不同的路由函数,因此可在同一路由里共存一般情况下,使用路由函数include就没必要再对路由设置参数name,尽管设置了参数name,但实际开发中没有实质的作用。
注: 在不同项目应用的路由命名可以重复的,这种命名是合理的。但在一个项目中,多条路由是允许使用相同的命名的,这种方式是不合理的。
在实际开发中,支持使用路由命名,因为网站更新或防止爬虫程序往往需要频繁修改路由地址,倘若在视图或其他功能模块里使用路由地址,当路由地址发生更新变换时,这些模块里所使用的路由地址也要随之修改,这样就不利于版本的变更和维护;相对而言,如果在这些功能模块里使用路由命名来生成路由地址,就能避免路由地址的更新维护问题。

3、路由的使用方式

3.1 在模板中使用路由

从网站开发的角度分析,网站代表路由,若想将项目定义的路由的路由显示在网页上,则要在模板上使用模板语法来生成路由地址。Django内置了一套模板语法,它能将Python的语法转换成HTML语言,然后通过浏览器解析HTML语言并生成相应的网页内容。
项目环境搭建后,在MyDjango文件夹的urls.py中使用urls.py中使用路由函数path和include定义项目应用文件夹index的路由,代码如下:

from django.contrib import admin
from django.urls import path,include
urlpatterns = [
    # 指向内置Admin后台系统的路由文件sites.py
    path('admin/',admin.site.urls),
    # 指向index的路由文件urls.py
    path('',include('index.urls'))
]

在项目应用的index里,分别在urls.py和view.py文件中定义路由和视图函数,并且在模板文件夹templates的index.html文件中编写模板内容,代码如下:

# index的urls.py
from django.urls import re_path,path
from . import views
urlpatterns = [
    # 添加带有字符类型、整型和slug的路由
    re_path('(?P<year>[0-9]{4})/(?P<month>[0-9]{2}/(?P<day>[0-9]{2})).html',views.mydate,name = 'mydate'),
    # 定义首页的路由
    path('',views.index)
]

# templates的index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
</head>
<body>
    <span>Hello World!</span>
    <br>
    <a href="{% url 'mydate' '2020' '04' '10' %}">查看日期</a>
</body>
</html>

现分析index.html的模板内容,模板使用了Django内置的模块语法url来生成路由地址,模板语法url设有4个不同的参数,其说明如下:

  • mydate: 代表命名mydate的路由。.
  • 2020: 代表路由地址变量year。
  • 04: 代表路由地址变量month。
  • 10: 代表路由地址变量day。
    注: 变量之间使用空格隔开。

模板语法的参数设置与路由定义是相互关联的,具体说明如下:

  • 若路由地址存在变量,则模板语法url需要设置相应的参数值,参数值之间使用空间隔开。
  • 若路由地址不存在变量,则模板语法url只需设置路由命名name即可,无需设置额外参数。
  • 若路由地址的变量与模板语法url的参数数量不相同,则在浏览器访问网页的时候会提示NoReverseMatch at的错误信息。

上述例子中,MyDjango文件夹的urls.py在使用函数include定义路由时并没有设置命名空间namespace。若设置了命名空间namespace,则模板里使用路由的方式有所变化。下面对MyDjango文件夹urls.py和模板文件夹templates的index.html代码进行修改:

# MyDjango文件夹的urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
    # 指向内置Admin后台系统的路由文件sites.py
    path('admin/',admin.site.urls),
    # 指向index的路由文件urls.py
    # path('',include('index.urls'))
    # 使用命名空间namespace
    path('',include(('index.urls','index'),namespace='index')),
]

# templates的index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
</head>
<body>
    <span>Hello World!</span>
    <br>
    {# <a href="{% url 'mydate' '2020' '04' '10' %}">查看日期</a>#}
    <a href="{% url 'index:mydate' '2020' '04' '10' %}">查看日期</a>
    
</body>
</html>

从模板文件index.html可以看出,若项目应用设有命名空间namespace,则模板语法url在使用路由,需要在命名路由name前面添加命名空间namespace并且使用冒号隔开,如:”namespace:name“。若路由在定义过程中使用命名空间namespace,而模板语法url没有添加命名空间namespace,则在访问网页时,Django会提示报错信息。

3.2 反向解析reverse与resolve

路由除了在模板里使用之外,还可以在视图里使用。我们知道Django的请求生命周期是指用户在浏览器访问网页时,Django根据网址在路由列表里查询相应的路由,再从路由里找到视图函数或视图类进行处理,将处理结果作为响应内容返回浏览器并生成网页内容。这个生命周期是不可逆的,而在视图里使用路由这一过程被称为反向解析。 Django的反向解析主要由函数reverse和resolve实现,函数reverse是通过路由命名或视图对象来生成路由地址的;函数resolve是通过路由地址来获取路由对象信息的。
在MyDjango文件夹的urls.py和index文件夹的urls.py里定义路由地址。代码如下:

# MyDjango文件夹的urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
    # 指向内置Admin后台系统的路由文件sites.py
    path('admin/',admin.site.urls),
    # 指向index的路由文件urls.py
    path('',include('index.urls','index'),namespace ='index'),
]

# index文件夹的urls.py
from django.urls import path
from . import views
urlpatterns = [
	re_path('<year>/<int:month>/<slug:day>',view.mydate,name='mydate'),
	# 定义路由的首页
	path('',views.index,name = 'index')
]

由于反向解析函数reverse和resolve常用于视图(views.py)、模型(models.py)或Admin后台(admin.py)等,因此在视图(views.py)的函数mydate和index里分别使用reverse和resolve,代码如下:

from django.http import HttpResponse
from django.shortcuts import reverse
from django.urls import resolve

def mydate(request, year, month, day):
    args = ['2019', '12', '12']
    result = resolve(reverse('index:mydate', args=args))
    print('kwargs:', result.kwargs)
    print('url_name:', result.url_name)
    print('namespace:', result.namespace)
    print('view_name:', result.view_name)
    print('app_name:', result.app_name)
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

def index(request):
    kwargs = {'year': 2010, 'month': 2, 'day': 10}
    args = ['2019', '12', '12']
    print(reverse('index:mydate', args=args))
    print(reverse('index:mydate', kwargs=kwargs))
    return HttpResponse(reverse('index:mydate', args=args))

函数index主要使用反向解析函数reverse来生成路由mydate的路由地址。为了进一步了解reverse,我们可以查看reverse的源码文件。
函数reverse设有必选参数viewname,其余参数是可选参数,各个参数说明如下:

  • viewname: 代表路由命名或可调用视图对象,一般情况下是以路由命名name来生成路由地址的
  • urlconf: 设置反向解析的URLconf模块。默认情况下,使用配置文件settings.py的ROOT_URLCONF属性(MyDjango文件夹的urls.py)
  • args: 以列表方式传递路由地址变量,列表元素顺序和数量应与路由地址变量的顺序和数量一致。
  • kwargs: 以字典方式传递路由地址变量,字典的键必须对应路由地址变量名,字典的键值对数量与变量的数量一致。
  • current_app: 提示当前正在执行的视图所在的项目应用,主要起到提示作用,在功能上并无实质的作用。

一般情况下只需设置函数reverse的参数viewname即可,如果路由地址设置变量,那么可自行选择参数args或kwargs设置路由地址的变量值。
接下来分析视图函数mydate,它是在函数reverse的基础上使用函数resolve。我们可以查看函数resolve源码,可以看到,resolve设有两个参数,参数path是必选参数,urlconf是可选参数。

  • path: 代表路由地址,通过路由地址来获取对应的路由对象信息。
  • urlconf: 设置反向解析的URLconf模块。默认情况下,使用配置文件settings.py的ROOT_URLCONF属性(MyDjango文件夹的urls.py)。函数resolve是以对象作为返回值的,该对象内置多种函数方法获取具体的路由信息。具体函数可网上搜索。

综上所说,函数reverse和resolve主要是对路由进行反向解析,通过路由命名和路由地址来获取路由信息。在使用这两个函数的时候,需要注意两者所传入的参数类型和返回值的数据类型。

3.3 路由重定向

重定向称为HTTP协议重定向,也可以称为网页跳转,它对HTTP状态码为301、302、303、307、308。简单来说,网页重定向就是在浏览器访问某个网页的时候,这个网页不提供响应内容,而是自动跳转到其他网址,由其他网址来生成响应内容。
Django的网页重定向有两种方式:第一种方式是路由重定向;第二种方式是自定义视图的重定向。 两种重定向方式各有优点,前者是使用Django内置的视图类RedirectView实现的,默认支持HTTP的GET请求;后者是在自定义视图的响应状态设置重定向,能让开发者实现多方面开发需求。
我们在MyDjango项目里分别讲述Django的两种重定向方式,在index的urls.py中定义trunTo,其代码如下所示:

from django.urls import path
from . import views
from django.views.generic import RedirectView

urlpatterns = [
    # 添加带有字符类型、整型和slug的路由
    path('<year>/<int:month>/<slug:day>', views.mydate, name='mydate'),
    # 定义首页的路由
    path('', views.index, name='index'),
    # 设置路由跳转
    path('trunTo', RedirectView.as_view(url='/'), name='trunTo'),
    # path('trunTo', RedirectView.as_view(pattern_name='index:index'), name='trunTo'),
]

在路由里使用视图类RedirectView必须使用as_view方法将视图类实例化,参数url用于设置网页跳转的路由地址,"/"代表网站首页(路由命名为index的路由地址)。然后在index的views.py中定义视图函数mydate和index,代码如下:

from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import reverse

def mydate(request, year, month, day):
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

def index(request):
    print(reverse('index:trunTo'))
    return redirect(reverse('index:mydate', args=[2019,12,12]))

视图函数index使用重定向函数redirect实现网页重定向的,这是Django内置的重定向函数,其函数参数只需传入路由地址即可实现重定向。
运行MyDjango项目,在浏览器上输入127.0.0.1:8000/turnTo,发现该网址首先通过视图类RedirectView重定向首页(路由命名为index),然后在视图函数index里使用重定向函数redirect跳转到路由命名为mydate的路由地址,如图所示:
在这里插入图片描述
从图中看出,浏览器的开发者工具记录了3条西悉尼,其中trunTo的请求信息是我们在浏览器输入的网址,而名为127.0.0.1的请求信息是网站首页,两者的HTTP状态码都是302,说明视图类RedirectView和重定向函数redirect皆能实现网站的重定向。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章