django路由层
django路由层的功能:路由分发,即url匹配对应的视图函数。核心代码书写在urls.py文件
路由匹配
# urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
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), # 尾页(了解)
]
-
url()
的第一个参数是正则表达式,只要该正则表达式能够匹配到内容,那么就会立刻停止往下匹配,直接执行该url对应的视图函数。 -
在浏览器页面输入url的时候,默认出现斜杠
/
的原因:浏览器发送过来的请求如果匹配不到视图函数,django内部会再有一个重定向的功能,重定向时自动在当前url尾部加一个/
;重定向再匹配不到则直接报错。 -
如果需要取消自动加
/
的功能,需要在配置文件中添加如下配置信息:APPEND_SLASH = False
无名分组
分组,就是给某一段正则表达式用小括号扩起来。语法:()
无名分组,就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数。
所以,后面的视图函数在书写时一定要增加一个形参,用来接收该位置参数,否则直接报错。
# urls.py
urlpatterns = [
url(r'^test/(\d+)/', views.test)
]
# views.py
def test(request, xx):
print(xx)
return HttpResponse('test')
有名分组
有名分组的功能和无名分组的功能类似,不过此时传递的是关键字参数。
具体做法,给分组起一个别名,这个别名就是后面视图函数的关键字形参
语法:(?P<nick_name>)
# urls.py
url(r'^testadd/(?P<year>\d+)', views.testadd)
# views.py
def testadd(request, year):
print(year)
return HttpResponse('testadd')
补充:
- 无名分组和有名分组不能在一个
url()
里面混合使用; - 一个
url()
里面可以多次使用有名分组或者多次使用无名分组。
url(r'^index/(\d+)/(\d+)/(\d+)/', views.index),
url(r'^index/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)/', views.index),
反向解析
反向解析的目的:
- 在模板文件或者视图层中优雅地获取一条完整有效的url,避免硬编码,减少程序维护的复杂度。
什么是反向解析:
- 前端浏览器发送过来一条url请求,该url会匹配到一个负责该请求的视图函数(可能同时给视图函数提供一些传参),此为正向匹配。
- 从视图函数绑定关系的别名出发(可能需要一些参数),寻找一条完整url的过程是反向,所谓解析就是通过别名(或者说是url匹配关系的别名,又或者url-pattern的别名)外加一些参数,获取一条完整的url。
正向匹配: url ---------> 视图函数(+参数)
反向解析: 别名(+参数) ---------> url
如何使用反向解析:
- 使用反向解析分两步(在路由匹配中
urls.py
设置别名;在模板文件或者视图中使用别名)
普通反向解析
设置反向解析:给url
起别名
# urls.py
url(r'^func_kkk/', views.func, name='ooo')
模版文件中使用:{% url 'ooo' %}
<a href="{% url 'ooo' %}">111</a>
视图函数中使用:reverse()
from django.shortcuts import render,reverse
def func(request):
....
this_url = reverse('ooo') # func_kkk/
-
上述操作是url没有分组时的反向解析操作,url涉及到分组时,需要单独考虑。
-
有名分组和无名分组,反向解析在
url.py
中的设置和没有分组时的设置操作是一致的,都是通过参数name
给当前url-pattern起一个别名。 -
如果存在分组:反向解析时,不仅需要提供别名,还需要提供分组中需要的数据。
-
一般用在数据的编辑和删除操作,通过分组提供操作数据的主键值,即这个在反向解析时提供的数据是编辑数据的主键值。
-
有名分组时反向解析时,提供数据的方式不论在前端还是在后端,都有两种方式:方式1是有分分组所特有的,方式2是有名分组和无名分组共有的(简便的写法)。
无名分组-反向解析
# url起别名
url(r'^index/(\d+)/', views.index, name='xxx')
# 前端反向解析
{% url 'xxx' 123 %}
# 后端反向解析
reverse('xxx', args=(1, )) # 无名分组,分组匹配到的数据通过args元祖接收
有名分组-反向解析
# url起别名
url(r'^func/(?P<year>\d+)/', views.func, name='ooo')
# 前端反向解析
<a href="{% url 'ooo' year=123 %}">111</a> # 方式1,了解
<a href="{% url 'ooo' 123 %}">222</a> # 方式2
# 后端反向解析
reverse('ooo', kwargs={'year':123}) # 方式1, 分组匹配到的数据通过kwargs字典接收
reverse('ooo', args=(111, )) # 方式2
路由分发
-
django的每一个app都可以有自己的
urls.py
文件、templates
文件夹、statics
文件夹。 -
基于这一点,django可以非常友好的实现多人协同开发,这其中路由分发功不可没。
作为组长,只需要将手下书写的app全部拷贝到一个新的django项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来
当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护
这个时候也可以利用路由分发来减轻总路由的压力
利用路由分发之后 总路由不再干路由与视图函数的直接对应关系
而是做一个分发处理
识别当前url是属于哪个应用下的 直接分发给对应的应用去处理
子路由urls.py
文件
# app01 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^reg/', views.reg)
]
# app02 urls.py
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^reg/', views.reg)
]
总路由urls.py
文件
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 路由分发方式1
url(r'^app01/', include(app01_urls)),
url(r'^app02/', include(app02_urls))
]
- 只要url前缀是
app01
开头,全部交给app01.urls.py
处理 - 只要url前缀是
app02
开头,全部交给app02.urls.py
处理 - 注意:总路由做路由分发时url()的正则表达式参数不能以
$
结尾,必须以/
结尾。 - 总路由分发简单写法:无需导入子路由,直接
include
子路由字符串
urlpatterns = [
# 路由分发方式2
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls'))
]
补充:
名称空间
当多个应用app出现了相同的别名,反向解析在识别应用前缀时会出现识别错误。这种情况下的反向解析是没有办法自动识别前缀的。解决的办法有两种:名称空间;别名唯一。
名称空间的使用很简单,就是在路由分发时(即include
里面)通过参数namespace
设置该路由所属的名称空间。
总路由:urls.py
url(r'^app01/', include('app01.urls', namespace='app01')),
url(r'^app02/', include('app02.urls', namespace='app02'))
反向解析时:提供名称空间即可
# 后端
reverse('app01:reg')
reverse('app02:reg')
# 前端
{% url 'app01:reg' %}
{% url 'app02:reg' %}
补充:
- 其实只要保证名字不冲突 就没有必要使用名称空间,这是解决问题的关键。
- 别名唯一化,app的名字加url的名字,如:
app01_reg
、app02_reg
- 补充中的补充:别名相同但
url_patterns
的参数个数不一样,也是可以反向解析的 - 有时还可以利用重名时仅仅匹配最后一个的特殊现象来覆盖已有的,定制化自己的。
伪静态
未静态:将一个动态网页伪装成静态网页。
目的:增大本网站的seo查询力度,并且增加搜索引擎收藏本网上的概率。
搜索引擎本质上就是一个巨大的爬虫程序, but:无论你怎么优化怎么处理, 始终还是干不过RMB玩家
具体方法:在url()
的第一个正则参数中加.html
urlpatterns = [
url(r'^reg.html', views.reg, name='app02_reg')
]
虚拟环境
# 在正常开发中,我们会给每一个项目配备一个该项目独有的解释器环境,该环境下只安装项目需要的模块。
# 虚拟环境,就类似于重新下载了一个纯净的python解释器
# 批量安装项目所需要的模块 requirements.txt
pip install -r requirements.txt
# 格式如下
Django==1.11.11
bs4==0.0.4
# 批量导出项目中使用的模块及版本
pip3 freeze >requirements.txt
django版本区别
区别1:django1.x路由层使用的是url()
、django2.x以后版本路由层使用的是path()
方法。
url()
第一个参数支持正则;path()
第一个参数是不支持正则的,写什么就匹配什么- django2.x及以后版本提供
re_path
(等同于url),且继续支持django1.x的url()
- 虽然path不支持正则,但path内可以使用转换器,默认自带5种转换器,且还可以自定义转换器。
from django.urls import path, re_path
from django.conf.urls import url
# urls.py
urlpatterns = [
re_path(r'^index/', index),
url(r'^login/',login)
path('index/<int:id>/', index) # <>内容匹配到的内容先转成整型,然后以关键字传参交给视图函数,且形参名必须是 id
]
# views.py
def index(request, id):
print(id, type(id))
return HttpResponse('index')
区别2:django1.x模型层外键默认都是级联更新删除的,django2.x以后版本需要手动配置。
question = models.ForeignKey(Question, on_delete=models.CASCADE)
转换器(django2.x)
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
自定义转换器:先定义,再注册,最后使用
# app01/path_converts.py
# 定义
class MonthConverter:
regex='\d{2}' # 属性名必须为regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
# urls.py
from django.urls import path, register_converter
from app01 import views
from app01.path_converts import MonthConverter
# 注册转换器
register_converter(MonthConverter, 'mon') # 注册时命名转换器为 'mon'
# 使用
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]