Django-5:django路由层
路由层
一、路由匹配
'''views.py'''
url(r'test',views.test),
url(r'testadd',views.testadd)
- url方法第一个参数是正则表达式,第二个为视图函数。
- 只要访问的url符合正则规则,可以匹配到内容时,那么就会停止往下匹配,直接执行对应的视图函数。
所以:
- 在上述代码中,testadd是永远访问不到的,因为访问127.0.0.1:8000/testadd时,会test也符合,所以并不会执行testadd的视图函数。
关于url方法,更详细的说明
''' urlpatterns = [ url(regex, view, kwargs=None, name=None), # url本质就是一个函数 ] 函数url关键参数介绍 regex:正则表达式,用来匹配url地址的路径部分, 例如url地址为:http://127.0.0.1:8001/index/,正则表达式要匹配的部分是index/ view:通常为一个视图函数,用来处理业务逻辑 kwargs:略(用法详见有名分组) name:略(用法详见反向解析) '''
django内部的重定向:
-
输入url的时候会默认加斜杠,如http://127.0.0.1:8000/testadd/
-
这个斜杠并不是浏览器添加的,而是django,如图:
-
当一次匹配不行时,django会再末尾添加一个斜杠,再次匹配路由
-
所以在写路由匹配时,可以在后面添加一个“/”斜杠,这样test与testadd就不会产生冲突
另外,url方法第一位参数是正则,那么可以根据正则表达式的"^“与”$",实现开头和结尾精细化匹配
'''views.py''' urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^test/',views.test), url(r'^testadd/',views.testadd) ]
如何去掉django的自动加斜杠(了解即可)
'''settings.py'''
APPEND_SLASH = False
- 需要手写上去,默认为True,表示自动添加斜杠。
所以项目中可以根据路由匹配规则,来细分为首页、尾页等。
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 首页
url(r'^$',views.home),
# 路由匹配
url(r'^test/$',views.test),
url(r'^testadd/$',views.testadd),
# 尾页(报错页面)
url(r'',views.error),
]
二、无名、有名分组
什么是分组、为何要分组
-
比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号)
此时可以使用 http://127.0.0.1:8001/article/?id=3,也可以直接将参数放到路径中,如: http://127.0.0.1:8001/article/3/
-
针对后一种方式,Django就需要直接从路径中取出参数,这就用到了正则表达式的分组功能了,分组又分为两种:无名分组与有名分组。
2.1 无名分组
格式:
# 小括号包裹起来的为一个分组。
url(r'正则/(正则)/',views.视图函数)
小测试,需求为:
-
用户在浏览器输入http://127.0.0.1:8000/test/+编号,可查看对应编号的文档内容。
注:
为了方便省事,本次测试中直接返回小段文本,正常情况下,用户输入的编号,将
作为查询主键值,随后通过该主键值去数据库中进行查找,然后返回。
路由层
url(r'^test/(\d+)/',views.test)
- \d 匹配数字
- + 重复1次或多次,默认为贪婪匹配(尽可能多的匹配)
视图层
def test(request,xx): # xx该形参用来接收,正则表达式中,匹配中分组的数据。
print(xx)
return HttpResponse('文档:%s'%(xxx))
-
xx形参用于接收符合正则表达式分组中的内容,该形参可自定义。
如:
http://127.0.0.1:8000/test/12345 那么形参 xx = 12345
在项目中,这个12345通常就为主键值
测试结果:
'''
浏览器访问 http://127.0.0.1:8000/test/789
浏览器:“文档:789”
后端:打印“789”
'''
-
分组中的 \d+ 值为789,随后交给xx形参。
-
执行视图函数内的代码。
此时应该拿着这个789这个值,去数据库中寻找id为789的数据,随后渲染到页面上返回给用户。
简单理解,无名分组就是将,括号内正则表达式匹配到的内容,当作位置参数传递给后面的视图函数
2.2 有名分组
格式:
# 与无名分组相比,有名可以给正则表达式起别名。
url(r'正则/?P<别名>正则表达式/',views.视图函数)
代码示例:
url(r'^testadd/(?P<file_id>\d+)',views.testadd)
def testadd(request,file_id):
print(year)
return HttpResponse('文档:%s'%(file_id))
# 有名分组的情况下,需要注意的是,给分组起的名字,要和视图函数中的形参对应上。
'''
有名分组就是将括号内,正则表达式匹配到的内容,当作关键字参数传递给后面的视图函数。
'''
2.3 总结
无名分组和有名分组的异同:
- 相同点: 都是将括号内的正则表达式,所匹配到的内容,当做位置参数或者关键字,传给后面的视图函数。
- 不同点: 无名分组没有具体的名字,作为视图函数中的形参时,可做任意名字,如‘XXX’。
有名分组,如,就必须是user。
无名有名不可以混用
-
但可以同时有多个无名或有名参数
# 路由 同时存在多个无名参数,有名参数同理,不过形参要改为**kwargs url(r'^test/(\d+)/(\d+)/(\d+)',views.test), # 视图 def test(request,*args): print(args) return HttpResponse('test') # 浏览器访问 http://localhost:8000/test/1/2/3 # 最终控制台输出结果: ('1', '2', '3')
三、反向解析
3.1 反向解析的概述和来由
'''
在Django中提供了关于URL的映射的解决方案,可以做两个方向的使用
1.普通解析过程:由客户端的浏览器发起一个url请求,Django根据url解析,把url中的参数捕获,调用相应的视图,获取相应的数据,然后返回给客户端显示。
2.反向解析:通过一个视图的名字,再加上一些参数和值,逆向获取相应的url。简单来说,反向解析(本质)就是指通过一些方法,得到一个结果,该结果可以访问到对应的url并触发视图函数的运行。
'''
反向解析的应用场景,是因为在软件开发初期,url地址的路径设计可能并不完美,后期需要进行调整,如果项目中很多地方使用了该路径,一旦该路径发生变化,就意味着所有使用该路径的地方都需要进行修改,这是一个非常繁琐的操作。
因此,解决方案就是在编写一条url(regex,view,kwargs=None,name=None)时,可以通过参数name为url地址的路径部分起一个别名,项目中就可以通过别名来获取这个路径。以后无论路径如何变化别名与路径始终保持一致。这种通过别名获取路径的过程就称为反向解析。
3.2 实验案例
本章节将通过小案例,来体验下反向解析可以实现的效果。
3.2.1 未使用反向解析的效果
'''
代码实现效果:
浏览器访问127.0.0.1:8000/index/ 可跳转到login页面,该页面为展示当前访问的路径。(伪需求)
'''
路由层:
urlpatterns = [
url(r'^index/$',views.index),
url(r'^login/$',views.login),
]
视图层:
def index(request):
return redirect('/login/')
def login(request):
return render(request,'login.html')
模板层:
<body>
<p>/login/</p>
</body>
'''
上述代码虽然确实可以实现效果,但是:
当url(r'^login/$',views.login)
改为url(r'^login_login/$',views.login)
此时页面跳转将会失败,因为return redirect('/login/') 而非 redirect('/login_login/'),这并不符合路由匹配规则,所以也就触发不了视图函数,得不到html页面。
另外按照要求,就算是得到了HTML页面,可是展示的内容仍然是‘/login/’,前端无法动态的获取路径数据。
'''
总结:
目前需要解决的问题:
(1)主页面下的子页面,该路径不可更改,否则无法跳转。
(2)前端如果需要接收访问路径,那么路由更改之后,前端不能动态的做出更改。
3.2.2 利用反向解析的效果
'''
利用反向解析
'''
路由层:
urlpatterns = [
url(r'^index/$',views.index),
url(r'^login/$',views.login,name='login_page'),
]
视图层:
from django.shortcuts import reverse # import reverse
def index(request):
return redirect(reverse('login_page')) # reverse('路径别名') = 路径别名对应的路径
def login(request):
return render(request,'login.html')
模板层:
<body>
<p>'{% url 'login_page' %}'</p> # 模板语法,将写死的路径,改为通过别名的方式动态获取,要注意加引号 "{%%}"
</body>
'''
此时不管login的路由怎么写,最后都可以执行视图层的login函数
'''
3.3 总结
'''反向解析(本质)就是指通过一些方法,得到一个结果,该结果可以访问到对应的url并触发视图函数的运行'''
# 先给路由与视图函数起一个别名
url(r'^abc/',views.func,name='XXX')
# 反向解析
# 后端反向解析
from django.shortcuts import render,HttpResponse,redirect,reverse
reverse('XXX')
# 前端反向解析
"{% url 'XXX' %}"
四、综合应用
'''
上文中提到:
分组和反向解析的作用,那么本章节,将详细介绍下无名分组的反向解析,和有名分组的反向解析。
'''
回顾:
- 分组的作用: 可以让我们通过url路径的方式,来获取参数。
- 反向解析: 可以让前后端,动态的获取url路径(IP+端口后面的那部分)
用法/格式:
-
分组:
-
# 无名分组 url('^/xxx/(正则)') # 有名分组 url('^/xxx/?P<别名>正则')
视图层的用法:
def xxx(request,XXX自定义) # 或 def xxx(request,有名分组的名字)
-
-
反向解析:
-
url(r'^路径/',views.视图函数,name='XXX') # 前后端通过反向解析可得到的路径为: /路径/
视图层的用法:
- 后端:通过别名获取路径信息,先导入reverse模块,随后reverse(别名名称),返回别名对应的路径信息。
- 前端:“{% url ‘别名名称’ %}” (注意单双引号)
-
通过下面的小需求,来对比下普通解析、反向解析、分组解析之间的异同
需求:
- 写一个用户数据展示的web页面,该页面可展示数据库中的所有数据,并且可进行增删改查。
- 要求采用**“将参数放到路径中”**的方式,如:http://ip:port/dict/1/…
注:
1.因为篇幅问题,前端页面只写核心代码
2.在后续案例中,只写需要修改的代码
4.1 应用案例:普通解析
# 模型
class User(models.Model):
id = models.AutoField(primary_key=True,verbose_name='uid')
username = models.CharField(max_length=32,verbose_name='username')
password = models.IntegerField(verbose_name='password')
# 路由
url(r'^index/',views.index),
url(r'^edit/',views.edit_user),
url(r'^delete/',views.delete_user),
# 视图
'''
首页
'''
from app01 import models
def index(request):
'''
:param request:
:return: 拿到数据库中的所有数据,返回为queryset对象
'''
data_queryset = models.User.objects.all()
return render(request,'index.html',locals())
'''
编辑功能
'''
def edit_user(request):
'''
通过携带参数的方式,获取前端用户想要修改数据的主键值,进而修改。
'''
user_id = request.GET.get('id')
user_obj = models.User.objects.filter(id=user_id).first()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
edit_obj = models.User.objects.filter(id=user_id).update(username=username, password=password)
return redirect('/index/')
return render(request,'index_edit.html',locals())
'''
删除功能
'''
def delete_user(request):
user_id = request.GET.get('id')
models.User.objects.filter(id=user_id).delete()
return redirect('/index/')
# 模板
'''
首页页面
'''
<table class="table table-striped table-hover">
<thead>
<tr>
<th>UID</th>
<th>Username</th>
<th>Password</th>
</tr>
</thead>
<tbody>
<!-- user_obj为数据库中每行的数据,id、username、password为数据库中数据的字段名 -->
{% for user_obj in data_queryset %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
<td>
<a href="/edit/?id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>
<a href="/delete/?id={{ user_obj.id }}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
'''
编辑页面
'''
<form action="" method="post">
<p>username:<input type="text" name="username" class="form-control" value="{{ user_obj.username }}"></p>
<p>password:<input type="text" name="password" class="form-control" value="{{ user_obj.password }}"></p>
<input type="submit" class="btn btn-info btn-block" value="编辑">
</form>
4.1.1 代码处理逻辑
黄线:
- 表示访问Index页面,用户从浏览器输入http://127.0.0.1:8000/index,路由匹配成功,执行index函数,该函数返回queryset对象并被前端页面 所接收,随后渲染成完整的页面,最后该页面通过django封装之后返回给浏览器。
红线:
- 表示点击编辑按钮的访问流程,由于数据库中数据不止一条,所以我们需要知晓用户想要编辑的是那一条记录,所以编辑与删除按钮,都是A标签,跳转的路径是携带参数的,该参数即为数据的主键值。随后,获取到想要编辑的原数据,返回给浏览器。
紫线:
表示提交修改信息,以及提交之后,重定向返回主页查阅的流程。
紫线1,编辑之后提交,通过路由层,执行对应的视图函数,修改数据库中的信息。随后重定向跳转页面,如紫线2,再一次的发起get请求,请求新的数据并渲染到页面上。
4.1.2 普通解析会遇到的问题
'''
由于每一行编辑功能,都是通过重定向,利用参数不参与路径匹配的原理,携带该行数据的主键值作为参数进行访问。那么,哪些地方的改动,会影响服务呢?
'''
普通解析会遇到的问题
- 当edit和delete的路由规则有变动时,前端点击编辑或删除按钮(即a标签)会报错,因为跳转的路径不符合路由匹配规则,并没有在Django中开放接口,进而就不会执行视图函数。
优化方法
-
可以使用反向解析进行优化,前后端通过一些方法,得到一个结果,该结果可以访问到对应的url并触发视图函数的运行。
( 模板语法或reverse) (别名对应的路径) (满足url匹配规则)
4.2 应用案例:反向解析
在4.1的代码基础之上,进行添加反向解析,让前后端可以动态的获取开放的路径
# 路由层
url(r'^edit/', views.delete_user,name='edit_page'),
url(r'^delete/', views.delete_user,name='delete_page'),
# 视图层
#<!-- user_obj为数据库中每行的数据,id、username、password为数据库中数据的字段名 -->
{% for user_obj in data_queryset %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
<td>
# 利用模板语法,实现前端反向解析
<a href="{% url 'edit_page' %}?id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>
<a href="{% url 'delete_page' %}?id={{ user_obj.id }}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
'''
由于在本案例中,只有前端需要利用别名的方式获取路径,所以只是在前端利用模板语法进行获取。
'''
4.3 应用案例:分组反向解析
回顾,分组的应用场景:
分组的应用场景:
比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号),可以使用 http://127.0.0.1:8001/article/?id=3,也可以直接将参数放到路径中,如: http://127.0.0.1:8001/article/3/
但是,4.1章节和4.2章节,使用的都是将前者,而不是后者这种,将参数写入到路径里的。
分组反向解析最终实现的效果
- 通过将参数以路径的方式传入,并且当edit与delete的路由匹配规则发生改变时,不会受其影响。
4.3.1 无名分组反向解析-前端
# 路由层
url(r'^index/', views.index,),
url(r'^edit/(\d+)', views.edit_user,name='edit_page'),
url(r'^delete/(\d+)', views.delete_user,name='delete_page'),
# 视图层
def edit_user(request,user_id):
'''
此时的形参user_id,接收的是路由规则中分组的数据,
即:模板层a标签跳转路径中,属于分组部分的数据==要修改数据的id主键值
'''
#user_id = request.GET.get('id') 在分组反向解析中,该步骤就不需要了,因为形参user_id就是主键
user_obj = models.User.objects.filter(id=user_id).first()
'''
filter(id=user_id)
id:数据库中的主键字段。 user_id:分组中的路径信息,在该案例中为数据的主键值。
'''
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 数据库更新操作
edit_obj = models.User.objects.filter(id=user_id).update(username=username, password=password)
# 重定向回主页
return redirect('/index/')
return render(request,'index_edit.html',locals())
def delete_user(request,user_id):
#user_id = request.GET.get('id')
models.User.objects.filter(id=user_id).delete() # 删除
return redirect('/index/')
# 模板层
{% for user_obj in data_queryset %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
<td>
<a href="{% url 'edit_page' user_obj.id %}" class="btn btn-primary btn-xs">编辑</a>
<a href="{% url 'delete_page' user_obj.id %}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
前端:无名分组反向解析
"{% url 'edit_page' user_obj.id %}"
- 这里的edit_page为别名,即/edit/
- user_obj.id 为本条数据的主键id值。
- 连在一起,就成了/edit/1,或者/edit/2......., 这样就刚好满足路由规则 url(r'^edit/(\d+)',...)
- 随后后端获取后面的主键值,完成数据查询等任务。
4.3.2 有名分组反向解析-前端
'''
整体代码与无名分组反向解析是一样的。
只是说需要有一点,那就是视图函数中,接收形参的名字要和有名分组的名字相同。
'''
url(r'^edit/(?P<user_id>\d+)/', views.edit_user, name='edit_page'),
def edit_user(request,user_id): # 此处用来接收匹配路由规则中的分组数据时,需要和有名分组的名字相同。
以上案例中,只涉及到了前端的分组反向解析,并未涉及到后端的。 后端的见4.3.3章节
4.3.3 分组反向解析-后端
案例需求:
- 两个页面,一个为home,一个为index。
- 当home页面上提交编号id后,重定向到index页面,同时输出该编号对应的文章。
<!--home.html-->
<form action="" method="post">
{% csrf_token %}
请输入要获取的文章编号:<input type="text" name="index">
<input type="submit">
</form>
<!--index.html-->
<span>文章id为: {{ index }} </span>
<div>对应从数据库中查询出来的数据</div>
# 路由层
url(r'^home/',views.home),
url(r'^index/(\d+)',views.index,name='index_path'),
# 视图层
def home(request):
if request.method == 'POST':
index_num = request.POST.get('index')
index_path = reverse('index_path',args=(index_num,)) #固定格式
# index_path = /index/id值,满足index的路由匹配规则。
return redirect(index_path)
return render(request,'home.html')
def index(request,index):
return render(request,'index.html',locals())
流程分析
-
当访问home时,执行视图层的home函数,并返回home.html。
当页面上提交数据后,该数据(id)由index_num变量所接收。
-
reverse(‘index_path’,args=(index_num,))
可理解为路径拼接,reverse反向解析可以拿到别名为index_path的路径,如: /index/
因为我们是要跳转到index页面,由于该页面的路由匹配规则为’^index/(\d+)',所以reverse这里还需要一个参数,否则不满足路由匹配规则。
-
所以最后Index_path的值为,/index/1、 /index/2、/index/6、/index/前端输入的id值…
因为符合index的路由规则,所以redirect重定向没有问题。
-
执行index视图函数,该函数的index参数负责接收id值,也就是正则分组的那部分内容,后续通过id去查询相应内容,并渲染到页面上。
4.3.4 总结
分组反向解析
后端的格式
-
reverse('路径别名',args=(主键值,))
前端的格式
-
"{% url 'edit_page' user_obj.id %}"
五、路由分发
路由分发可以实现的效果:
-
多个应用内都创建urls.py文件,每个应用都是都负责单独处理一个功能,当用户访问时,根据访问前缀来将该请求交给指定的应用来处理。
所有的请求将不再直接通过主路由进行查询对应关系。
-
示例: 1. 现在有一个网站,http://ip:port/shop/info.html http://ip:port/user_center/info.html 上述两个info.html 页面分别代表着商店模块下的信息页面,和用户中心模块下的信息页面。 2. 路由分发所要实现的效果就是,将用户访问一个页面的时候,先看看是哪个功能模块(应用)相关的,随后交给该应用的路由来处理。 如:访问的资源是以shop开头的,还是user_center开头的。
路由分发使用方法
在应用下创建urls.py文件,默认是没有该文件的,所以需要拷贝总路由的urls代码
1.主路由 from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^app01/',include('app_01.urls')), url(r'^app02/',include('app_02.urls')), ]
- 引入include模块
- 后续只要访问是以app01开头,就到app_01的urls中去查询与视图的映射关系。
- 只要访问是以app02开头,就到app_02的urls中去查询与视图的映射关系。
2.子路由 # app_01应用的路由 from django.conf.urls import url from app_01 import views urlpatterns = [ url(r'^info/',views.info), ] # app_02应用的路由 from django.conf.urls import url from app_02 import views urlpatterns = [ url(r'^info/', views.info), ]
当访问的url是以app_01开头,并且后面跟的是info时,执行视图函数info。app_02同理
如: http://127.0.0.1:8000/app01/info/
http://127.0.0.1:8000/app02/info/
3.视图 # app_01应用的视图 def info(request): return HttpResponse('app01 info') # app_02应用的视图 def info(request): return HttpResponse('app02 info')
最终效果:
- 浏览器访问 http://localhost:8000/app01/info/ 页面展示内容:“app01 info”
- 浏览器访问 http://localhost:8000/app02/info/ 页面展示内容:“app02 info”
5.1 其他
在使用路由分发时,如果需要使用到反向解析,那么在起别名的时候就不可以重名,最好以应用名开头,这样就不会重复,就不会出现因为名称空间造成一些问题。
1.app_01路由
urlpatterns = [
url(r'^index/',views.app01,name='app01_index')
]
2.app_02路由
urlpatterns = [
url(r'^index/',views.app02,name='app02_index')
]
六、伪静态
并非django的知识点,只是一个可以增大本网站的seo查询力度的一个小技巧(当然了,不如RMB好使)
静态网页:
- 数据是写死的,不会进行更改。
伪静态:
伪静态就是将一个动态网页,伪装成静态网页。
伪装之后,可以增大本网站的seo查询力度
在Django中,如何实现伪静态的效果:
urlpatterns = [
url(r'^reg.html',views.reg)
]
这样在访问的时候,就变成了 http://127.0.0.1/reg.html
八、虚拟环境
在正常开发中,我们会给每一个项目配备一个该项目独有的解释器环境,该环境内只有该项目用到的模块,用不到一概不装。
同时每个项目的django版本可能还都不一样,1.x版本和3.x、4.x都有不同,所以对于不同版本的项目,需要使用虚拟环境区分开。
虚拟环境:
- 创建一个虚拟环境就类似于重新下载了一个纯净的python解释器。
- 但是虚拟环境不要创建太多,因为是需要消耗硬盘空间的。
"""
扩展:
每一个项目都需要用到很多模块 并且每个模块版本可能还不一样
那我该如何安装呢? 一个个看一个个装???
开发当中我们会给每一个项目配备一个requirements.txt文件
里面书写了该项目所有的模块即版本
你只需要直接输入一条命令即可一键安装所有模块即版本
"""
pycharm创建虚拟环境
找到新建项目,选中虚拟创建。点击创建之后,稍等即可(相当于重新下载一个python解释器)
此时会发现多了一个venv文件夹,这个是虚拟环境的标志,如果左侧有venv,那么说明用的是虚拟环境。
初始的虚拟环境只有这些模块,后续可根据需求,自行安装需要的模块
觉得下载模块的速度慢,可以更换pip源为清华源 https://pypi.tuna.tsinghua.edu.cn/simple/
创建好的虚拟环境,后续可以在创建项目的时候使用
九、django版本区别
一、路由匹配
1.django1.X路由层使用的是url方法
而在django2.Xhe3.X版本中路由层使用的是path方法
url()第一个参数支持正则
path()第一个参数是不支持正则的 写什么就匹配什么
如果你习惯使用path那么也给你提供了另外一个方法
from django.urls import path, re_path
from django.conf.urls import url
re_path(r'^index/',index),
url(r'^login/',login)
2. X和3.X里面的re_path就等价于1.X里面的url
3.虽然path不支持正则 但是它的内部支持五种转换器
path('index/<int:id>/',index)
# 将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数
def index(request,id):
print(id,type(id))
return HttpResponse('index')
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
4.除了有默认的五个转换器之外 还支持自定义转换器(了解)
class MonthConverter:
regex='\d{2}' # 属性名必须为regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
from django.urls import path,register_converter
from app01.path_converts import MonthConverter
# 先注册转换器
register_converter(MonthConverter,'mon')
from app01 import views
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
4.模型层里面1.X外键默认都是级联更新删除的
但是到了2.X和3.X中需要你自己手动配置参数
models.ForeignKey(to='Publish')
models.ForeignKey(to='Publish',on_delete=models.CASCADE...)