系列文章目录
文章目录
一、路由层基础
django的路由写在urls.py中的urlpatterns列表中。
当收到请求时,依次匹配每个URL模式,在与所请求URL相匹配的第一个路由停下来,接着执行对应的视图代码。
-
添加路由:
在urls.py文件中:
from django.contrib import admin from django.urls import path # 导入视图模块 from app名 import views urlpatterns = [ path('admin/', admin.site.urls), # 系统自带,管理后台相关,不要管 # 添加路由 path('url后缀部分/', views.视图函数), ]
1. 捕获参数
使用get方法提交的数据,保存在url中,因此我们需要通过一些方法来捕获这些数据,而方法就是使用django的<>
语法。
比如捕获数字:
path('index/<int:number>/', views.视图函数),
<>
用来捕获参数,冒号前面的int是路径转换器,用来匹配数字,它是可选的,不写的话默认转换为str路径转换器。
捕获到的参数会以关键字参数的形式传递给视图,参数名为冒号后面的内容。
2. 路径转换器
str
- 匹配除了'/'
之外的非空字符串,是默认值。返回str类型的变量。int
- 匹配0或任何正整数,返回int类型变量。slug
- 匹配任意由 ASCII 字母或数字以及连字符-
和下划线_
组成的字符串。uuid
- 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00
。path
- 匹配非空字段,包括路径分隔符'/'
。它允许你匹配完整的 URL 路径而不是像str
那样只匹配 URL 的一部分。
二、自定义路径转换器
对于更复杂的匹配需求,我们可以定义自己的路径转换器。
转换器是一个类,包含如下内容:
- 字符串形式的
regex
类属性。 to_python(self, value)
方法,它会将处理匹配到的字符串,转换为应该传递到视图函数的类型。如果没有转换为给定的值,它应该会引发ValueError
。ValueError
被解释为不匹配,因此向用户发送404响应。to_url(self, value)
,处理将 Python 类型转换为 URL 中要使用的字符串,显示在浏览器的地址栏中。
例如:
class FourDigitYearConverter:
regex = '[0-9]{4}' # 正则表达式,用来规定匹配规则
def to_python(self, value):
return int(value) # 转化为合适的数据类型
def to_url(self, value):
return '%04d' % value # 以合适的格式返回数据
在 URLconf 中使用 register_converter()
来注册自定义的转换器类:
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),
...
]
三、re_path()方法
python中正则表达式的语法:传送门
-
re_path()
方法:path()
方法的url模式是固定的字符串,不能使用正则表达式。
如果需要使用正则表达式,可以改用re_path()
方法。该方法需要导入:from django.urls import re_path
。但是,使用正则表达式的
re_path
有个缺点,比如:re_path(r'in', views.in), re_path(r'index', views.index),
在url后缀为
index
的情况下,依旧会先匹配到in
,不再去匹配index
,然后执行错误的index
视图。解决方法为在匹配规则的后面加一个斜杠即可:
re_path(r'^in/$', views.in), re_path(r'^index/$', views.index),
并且,为了更加严谨起见,最好在加一个
^
和$
表示严格以该模式作为起始和结束。 -
空白url的正确写法:
另外,我们通常会将没有后缀的url,也当作主页处理,让其与
index/
使用同一个视图。但匹配规则写空字符串''
,反而会匹配到所有的url后缀,不能这样用。应该采用下面的写法:re_path(r'^$', views.index), # 匹配真正的空白url后缀
-
取消url自动加斜杠:
如果我们在浏览器打开的url后缀为
aaa
,而路由中的匹配规则为aaa/
时。由于aaa
后面缺少了一个斜杠,照理来说应该匹配不到url模式。但事实却与之相反,aaa
不仅能匹配到aaa/
,而且在浏览器的地址栏中,aaa
的后面被自动加了/
。这是由于django帮我们做了优化,在
aaa
匹配不到url模式时,它会自动给aaa
加一个斜杠,使其成为aaa/
,然后请求浏览器重定向到该url,如果成功匹配到,就万事大吉,执行对应视图代码。如果匹配不到,这时候才会返回404。该功能是默认开启的,如果想要取消该功能,可以在settings.py中加入下面的内容:
APPEND_SLASH = False # 默认为True
四、有名分组、无名分组
如果想在re_path()
方法中提取url中的内容,可以使用正则表达式中的分组语法。
正则表达式分组:
(模式)
表示分组,即捕获模式匹配到的字符串。
(?P<名称>模式)
表示分组并命名,即捕获模式匹配到的字符串并命名。
-
无名分组:
比如从url中提取数字:
re_path(r'list/(\d+)/', views.list)
捕获到的数字会被直接当作位置参数传递给视图,因此,需要对应的参数来接收,并且要注意捕获分组的顺序与形参的顺序要一致:
def list(request, number): pass
-
有名分组:
还是以提取数字为例,只不过这次采用给分组命名的形式捕获字符串:
re_path(r'list/(?P<number>\d+)/', views.list)
这次,参数会以关键字参数的形式传递给视图,而视图就不需要按照位置接收参数,形参写分组的名称即可。分组的名称此时就相当于变量名。
注意:无名分组和有名分组不能混用!!!
五、反向解析
- 反向解析的作用:
通过名称来反向动态获取url路径。这样可以避免url需要修改时,一个个地去修改url,只需到urls.py中修改一次即可。
1. 基本使用方法
先使用name参数,给路由起一个名字:
path('index/', views.view_index, name='name_index')
然后通过反向解析,得到'/index/'
:
<a href="{% url 'name_index' %}"></a>
<!-- 以上代码相当于下面的代码 -->
<a href="/index/"></a>
在视图中使用反向解析:
from django.urls import reverse
def my_view(request):
reverse('name_index') # 该方法的返回值为:/index/
2. url参数的传递
如果需要从url中捕获参数,那么在反向解析的时候,就要向reverse()
和{% url %}
手动传递这些参数。根据位置参数和关键字参数两种捕获方法,有以下两种对应的传参方法:
-
位置参数:
在路由中:
re_path('index/(\d+)/', views.view_index, name='name_index')
在模板中:
<a href="{% url 'name_index' 888 %}"></a> <!-- 以上代码相当于下面的代码 --> <a href="/index/888/"></a> <!-- 对于需要用模板语法获取的参数,不需要写 {{ }} --> <!-- 错误写法 <a href="{% url 'name_index' {{ user.id }} %}"></a> --> <!-- 正确写法 --> <a href="{% url 'name_index' user.id %}"></a>
在视图中:
def my_view(request): reverse('name_index', args=(888,)) # 该方法的返回值为:/index/888/
注意:args参数接收的是元组类型实参,所以在只有一个参数的情况下,要加一个额外的英文逗号。
-
关键字参数:
在路由中:
path('index/<int:id>/', views.view_index, name='name_index')
在模板中:
<a href="{% url 'name_index' id=888 %}"></a> <!-- 以上代码相当于下面的代码 --> <a href="/index/888/"></a> <!-- 对于需要用模板语法获取的参数,不需要写 {{ }} --> <a href="{% url 'name_index' id=user.id %}"></a>
在视图中:
def my_view(request): reverse('name_index', kwargs={'id':888}) # 该方法的返回值为:/index/888/
在关键字参数的情况下,使用位置参数下的传参方法是可以的,只不过上面的方式更加正规。
六、路由分发
django的每一个app,也可以拥有自己的templates文件夹、static文件夹和urls.py文件。
-
路由分发的作用:
当一个django项目中的url特别多的时候,可以将url写在app的路由中,而在总路由(项目的urls.py)中指向对应app的路由,以此来减轻总路由的压力。 -
使用方法:
手动在app文件夹下创建urls.py文件,并将项目路由文件中的内容复制过来,然后删除不需要的内容。
比如,在项目urls.py中,只要是url后缀是
index/
开头的,都去appindex_app
中的urls.py中进行匹配;login/
开头的,都去applogin_app
下的urls.py中进行匹配;则总路由的写法为:
from django.urls import include, path urlpatterns = [ path('index/', include('index_app.urls')), path('login/', include('login_app.urls')) ]
上面的代码,**会将url后缀中的开头部分截取掉,将剩余部分发送给对应app的urls去处理。**比如,
index/xxx/
和login/yyy/
截取后为xxx/
和yyy/
。然后app中的路由,就按照路由的一般写法来就可以了。要注意的是:使用
re_path()
时,正则表达式不能写$
,因为写了$
,就不能匹配到除开头部分以外的内容了。
七、名称空间
假设我们有两个app,这两个app下都有主页index
,并且都给路由起了相同的名字index
,那么在反向解析的时候,django就分不清index
指的是哪个app下的主页了。而名称空间就是用来解决这一问题的。
-
总路由:
假设app的名称为
app01
。path('index/', include(('app01.urls', 'app01'), namespace='ind'))
与之前
include()
的参数不同的是,第一个参数改为了元组,元组内的元素指定了urls的位置和app的名称。第二个参数namespace用来设置名称空间。 -
app内的子路由:
path('yyy/', views.index, name='y')
-
反向解析:
视图中:
reverse('ind:y')
模板中:
{% url 'ind:y' %}
八、伪静态
伪静态就是将一个动态网页伪装成静态网页。目的是增大该网站的SEO(Search Engine Optimization,汉译为搜索引擎优化)力度,增加被搜索引擎搜到的概率,提高搜索引擎的展示优先级别。当然,无论我们怎么优化,还是干不过人民币玩家的。
-
实现伪静态:
只需要在url中加一个
.html
后缀即可。path('index.html/', views.index)