Django之路由层

文章目录

路由匹配

路由匹配的特点是,只要匹配上来 就会立刻结束执行对应的视图函数

语法

Django2、3用的路由匹配都是path,完整语法path('网址后缀',函数名)

Django1用的路由匹配是urls,完整语法url(r'^网址后缀', 函数名)

python 复制代码
	在django1.x版本中,路由 第一个参数是正则表达式,第二个参数是函数名
		url(r'^admin/', 函数名),

	在django2.x及以上版本:path第一个参数写什么就匹配什么(精准匹配),匹配到直接执行对应的视图函数
		path('admin/',函数名),  '这种写法不支持正则表达式'

	当然2.x版本也有可以使用正则匹配的方式:
		re_path(正则表达式,函数名) '其作用和django1.x的url使用效果一模一样'

路由配置注意事项

python 复制代码
	1.正则表达式不需要添加一个前导的反斜杠,因为每个URL都有。例如,应该是^index而不是 ^/index

	2.每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是"原始的" ------ 字符串中任何字符都不应该转义

	3.如果我们想匹配的路径就只是index/,那么正则表达式应该有开始符与结束符, 如 ^index/$。这样逻辑才算严谨

	4.路由结尾的斜杠
		我们在输入网址的时候与正确网址确实一个/ 但是成功进去了
			默认情况下不写斜杠 django会做二次处理
			第一次匹配不上,会让浏览器加斜杠再次请求
		'''
		如果我们想要少了'/'则进不去就得去django配置文件中指定是否自动添加斜杠(到settings文件中末尾添加)
		APPEND_SLASH = False,
		当配置文件中没有这个配置时,APPEND_SLASH的值是True
		'''

	'''
	注意: 在输入一次url地址后,浏览器会将其记录到缓存中,若第二次输入相同的url地址,
	浏览器也会直接在url地址后	填充/结尾,如果想规避浏览器缓存的影响,
	可以像下面这么测试,并且每次测试都换一下?后的变量,保证url地址每次都不同
	http://127.0.0.1:8000/index?aaaaa=111111     对应视图函数index1
	http://127.0.0.1:8000/index/?aaaaa=22222  对应视图函数index2
	'''	

'由于第一个参数是正则,所以当项目特别大,对应关系特别多的时候要格外注意是否会出现路由顶替的现象'

小拓展:
	可以定制一个主页面,用户不懈怠后缀可以直接访问
		url(r'^$',views.index)
	也可以定义一个尾页,用户输入一个没有对象关系的直接返回
		url(r'.*',views.error)

注意事项五:

按照匹配规则,是从上到下只要正则表达式匹配成功,就不会往下执行。

例子:

python 复制代码
	当浏览器要打开下面的web网页接口 ?
	URL=127.0.0.1:8000/testadd
	
	路由层代码展示:
	urlpatterns = [
	    url('test',views.test),
	    url('testadd',views.testadd)
	]

	视图层代码展示:
	def test(request):
    	return HttpResponse('from test')

	def testadd(request):
	    return HttpResponse('from testadd')

当路由匹配到第一个正则(test)就成功匹配了,就不会继续往下执行。因为按照路由的匹配规则,从上到下只要正则表达式匹配成功,就不会继续往下执行了

如何解决上述问题?

方式1:

python 复制代码
	我们在输入URL默认会加斜杠,因为django内部帮我们做了重定向,一次匹配不行,url后面加斜杠再来一次

	按照下面的顺序依次匹配
	    url('test/',views.test),
	    url('testadd/',views.testadd)

方式2:

python 复制代码
	settings配置文件内添加(控制django是否自动添加斜杠匹配
	
	APPEND_SLASH = False/True   # 默认是True自动添加斜杠

事项五其本质还是因为url方式是使用得正则表达式来匹配的原因 ,如果真正想要精准匹配到想要的那最好是给网址后缀加上开始符和结束符如url('^index/$')。

转换器

python 复制代码
	正常情况下很多网站都会有很多相似的网址,如果我们每一个都单独开设路由不合理
	django2.x及以上版本路由动态匹配有转换器(五种)
		str:匹配除路径分隔符外的任何非空字符串。	StringConverter()
		int:匹配0或者任意正整数。	IntConverter()
		slug:匹配任意一个由字母或数字组成的字符串。	SlugConverter()
		uuid:匹配格式化后的UUID。	UUIDConverter()
		path:能够匹配完整的URL路径。	PathConverter()
			'还支持自定义转换器(自己写正则表达式匹配更加细化内容)'
			
	'转换器 将对应位置匹配到的数据转换成固定的数据类型'
	路由层:
	path('index/<int:year>/<str:info>/',views.index)
	转换器有几个名字,那么视图函数的形参就必须对应
	视图层:
	def index(request,year,info):
	    print(year,info)
	    return HttpResponse('from index')

注册自定义转化器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

例子:

python 复制代码
	class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value

使用register_converter 将其注册到URL配置中:

python 复制代码
	from django.urls import register_converter, path  
	from . import converters, views  
	register_converter(converters.FourDigitYearConverter, 'yyyy')  
	urlpatterns = [  
	    path('articles/2003/', views.special_case_2003),  
	    path('articles/<yyyy:year>/', views.year_archive),  
	    ...  
	]

无名分组和有名分组

无名分组

​ 分组就是给某一段正则表达式用小括号括起来

python 复制代码
	from django.urls import path,re_path
	from app01 import views
	
	urlpatterns = [
		re_path('^test/(\d{4})/', views.test)
	]
	无名分组:会将括号内正则表达式匹配到的内容当做位置参数传递给视图函数

注意:

  • 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
  • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
  • 每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是"原始的" ------ 字符串中任何字符都不应该转义
  • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续

代码实现

python 复制代码
	urls.py(路由文件)
	'会把括号中匹配的数字当成位置参数传给视图函数'
	path('^test/(\d(4))/',views.test)
	
	views.py(视图文件)
	def test(request,xxx):
	    print(xxx)
	    return HttpResponse('from test')
	  
	正则:    
	\d(4)	: 匹配4个任意数字 

有名分组

上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。
在Python 正则表达式中,命名正则表达式组的语法是(?Ppattern),其中name 是组的名称,pattern 是要匹配的模式。

python 复制代码
	from django.urls import path,re_path
	from app01 import views
	
	urlpatterns = [
		re_path('^testadd(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.testadd)
	]
	有名分组:会将括号内正则表达式匹配到的内容当做关键字参数传递给视图函数
	(捕获的值作为关键字参数而不是位置参数传递给视图函数。)
	注意上述的分组不能混合使用!!!

	def testadd(request,year,month):  # 必须使用分组名才能接收到
	    print(year,month)
	    return HttpResponse(f'捕获到的年份:{year}<br/>捕获到的月份:{month}')

注意:有名分组可以允许有多个分组名,但不允许一个无名分组一个或者多个有名分组存在。即有名分组和无名分组不能混合是使用

单个有名或者无名分组是可以使用多次的

python 复制代码
	'''无名分组与有名分组不建议混合使用'''
	path('^index/(\d(4))/(?P<year>[a-z])/$',views.index)  # 不建议

	1.无名分组单个使用多次
	path('^index/(\d(4))/(\d(4))/(\d(4))/',views.index),
	
	2.有名分组单个使用多次
	path('^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),
	
	def index(request,*args,**kwargs):
	    print(args)
    	return HttpResponse('index')

反向解析

简介

在使用Django 项目时,一个常见的需求是获得URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。

在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

  • 在模板中:使用url 模板标签。
  • 在Python 代码中:使用from django.urls import reverse函数

反向解析为的就是防止路由经常变动,这样我们页面的链接、或者函数的重定向就能动态获取到路由可接收的URL。

普通反向解析

要想达到反向解析也很简单,给路由设置一个别名

python 复制代码
	from django.urls import path
	from app import views

	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('index/',views.index,name='index_name'),
	    path('login/',views.login)
	]

此时如果模板页面需要指向这个URL提交请求的话,且这个接收的URL是不固定的,那么我们就可以使用这个路由个别名来获取它可以接收的URL。

python 复制代码
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>Title</title>
	</head>
	<body>
	    <h1>登录</h1>
	    <a href="{% url 'index_name' %}">点击进入首页</a>
	</body>
	</html>

此时我们页面不管这个别名为:index_name的路由可接收的URL怎么变动,我们都可以直接指向它。
并且,重定向也是可以直接指向到路由别名的。

python 复制代码
	from django.shortcuts import reverse  # 导入模块
	def index(request):
    	return HttpResponse('from index')

	def login(request):
	    print(reverse('index_name'))
	    return render(request,'index.html')

但是!如果路由可接收的URL里面存在无名或者有名分组时,就不能这样操作了

无名分组、有名分组之反向解析

在Django1.x中,路由中使用的url作为匹配,并且URL匹配可以使用正则

而到了Django2.x、Django3.x则改为了path匹配,只有URL完全相同的才能匹配上。

但是Django2.x、Django3.x也可以使用URL匹配使用正则,那就需要使用:re_path

路由层:urls.py

python 复制代码
	from django.urls import path
	from app import views

	urlpatterns = [
	    path('admin/', admin.site.urls),
	    re_path('index/(?P<jump>[0-9])',views.index,name='index_name'),
	    path('login/',views.login)
	]

此时我们的HTML文件如果要指定某个路由,但是这个路由还必须要接收一个参数,那么我们只能在后面手动指定了

python 复制代码
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>Title</title>
	</head>
	<body>
	    <h1>登录</h1>
	    <a href="{% url 'index_name' 6 %}">点击进入首页</a>
	</body>
	</html>

而视图函数如要要动态指向这个路由可接收的URL的话,同时也要向它传递一个参数,将整个URL给补全:

python 复制代码
	from django.shortcuts import reverse  # 导入模块
	def index(request,jump):
	    print(jump)
	    return HttpResponse('from index')

	def login(request,):
	    # reverse可以根据别名找到路由可接收的URL,args则是将URL需要的额外参数填进去了
	    print(reverse('index_name',args=(1,)))
	    return render(request,'index.html')

上序操作归总:

python 复制代码
	无名分组的URL:
		路由:url(r'^index/(\d+)/',views.index,name='index_name')
		后端:reverse('index_name',args=(1,))  # 只要给个数字即可
		前端:<a href="{% url 'index_name' 1 %}"></a>  # 只要给个数字即可
    
	有名分组的URL:
		路由:url(r'^index/(?P<id>\d+)/',views.index,name='index_name')
		后端:reverse('index_name',kwargs={'id':123})  # 只要给个数字即可
		前端:<a href="{% url 'index_name' id=666 %}"></a>  # 只要给个数字即可
	    
		在后端与前端加上关键字是为了更好辨识而已
	
	总结
		无名有名都可以使用一种(无名)反向解析的形式

路由分发

简介

django是专注于开发应用的,当一个django项目特别庞大的时候所有的路由与视图函数映射关系全部写在总的urls.py很明显太冗余不便于管理,其实django中的每一个app应用都可以有自己的urls.py、static文件夹、templates文件夹,基于上述特点,使用django做分组开发非常的简便。

可以分开写多个app应用,最后再汇总到一个空的Django项目然后使用路由分发将多个app应用关联起来。

未使用路由分发前:所有请求都由主路由转发到对应视图函数,全部都在一个urls.py文件内。

使用路由分发后:将请求的URL由主路由转发到其它负责这个URL的子路由上面去。每个子路由文件都应该存放在对应的app应用下。

为什么要用路由分发?

  • 解决项目的总路由匹配关系过多的情况
  • 总路由分开于干路由与视图函数的的直接对应关系
  • 总路由是一个分发处理(识别当前url是属于哪个应用下的,直接分发对应的应用去处理)
  • 当请求来了,总路由不做对应关系,根据请求直接访问哪个app的功能,直接将请求发送给对应的app
  • 提前创建好应用app01(创建即注册)、app02,然后记得注册app02

路由分发实现

首先我们创建一个新的Django项目,然后创建两个app应用,保持如下目录结构:

也别忘记在settings配置文件中注册应用

templates文件夹路径也需要配置一下

此时我们开始配置主路由,分配URL请求转发给哪个子路由执行;主路由在我们的Django项目文件夹内urls.py

python 复制代码
	from django.urls import path,include  # 导入一个include路由分发模块
	from app import views
	
	# 导入子路由的url,避免重命,取别名
	from app import urls as app_urls
	from app02 import urls as app02_urls  # 增加别名是因为我们导入的两个urls名称相同
	
	
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('home/',include(app_urls)),  # 将ip+port/home/ 以这些为起始请求交给app>urls.py路由处理
	    path('userinfo/',include(app02_urls)),

注意:如果使用的是url作为路由的话,切记路由规则结尾不能使用$

此时我们分别配置两个应用下的子路由、视图函数、模板文件。

app>urls.py

python 复制代码
	from django.urls import path
	from app01 import views
	
	urlpatterns = [
	    path('index/', views.index),
	]

app>templates>index.html

python 复制代码
	<!DOCTYPE html>
	<html lang="en">
	      <head>
	          <meta charset="UTF-8">
	          <title>index</title>
	      </head>
	      <body>
	        <h1>This Is App01 Index</h1>
	      </body>
	</html>

app>views.py

python 复制代码
	from django.shortcuts import render

	def index(request):
	    return render(request,'index.html')

app02>urls.py

python 复制代码
	from django.urls import path
	from app02 import views
	
	urlpatterns = [
	    path('login/', views.login)
	]

app02>templates>login.html

python 复制代码
	<!DOCTYPE html>
	<html lang="en">
	      <head>
	          <meta charset="UTF-8">
	          <title>login</title>
	      </head>
	      <body>
	        <h1>This Is App02 Login</h1>
	      </body>
	</html>

app02>templates>views.py

python 复制代码
	from django.shortcuts import render

	def login(request):
	    return render(request,'login.html')


注意:port一定要是本机的Django服务端口。

上面主路由配置难免不够简单,随着子路由越来越多,导入的模块也越来越多了,继续缩短写法!

python 复制代码
	from django.urls import path,include  # 导入一个include路由分发模块
	
	urlpatterns = [
    path('admin/', admin.site.urls),
    path('home/',include('app.urls')),  # 将ip+port/home/ 以这些为起始请求交给app>urls.py路由处理
    path('userinfo/',include('app02.urls')),

当一个Django项目存在多个app应用,使用路由分法是很有必要的,减少了单个文件代码冗余,更便于管理。


伪静态的概念

伪静态: 其实就是把动态页面改成静态页面

python 复制代码
	伪静态: 其实就是把动态页面改成静态页面
	动态页面:
		"""它不是在html页面中写死的,它是根据后端的数据变化而变化"""
	    我们的页面上的数据是从数据库查询出来都是可以是动态页面
	    
	静态页面:
		html
	    css
	    
	案例:
		https://www.cnblogs.com/bigsai/p/17827160.html # 这个就是伪装之后的页面
	伪装的目的:
		"""为了更好的被各大搜索引擎抓大,静态页面是最容易被抓到的,有个别的网站就会做伪装,seo"""
	    seo:优化关键词被容易搜索到
	    sem: 广告(RMB)
	怎么样去伪装	 
		url(r'^v1/v2/test.html/$', views.test)  # 加上了html后缀

名称空间

其实每个路由都可以有属于自己的名称空间,只是目前我们并没有使用到而已。请见如下例子:

如果当多个应用在反向解析的时候出现了路由别名冲突的情况,那么就会无法识别。

这些HTML文件向根据路由别名来动态指向URL,但是此时别名冲突了,我们该如何解决呢

python 复制代码
	<a href={% url 'index_name' %}>app01应用</a>
	<a href={% url 'index_name' %}>app02应用</a>

解决方式一:名称空间,总路由内定义

python 复制代码
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('home/', include('app01.urls',namespace='app01')),
	    path('userinfo/', include('app02.urls',namespace='app02'))
	]

那么HTML文件可以根据主路由设置的名称空间,正确找到其下面的子路由的别名;

python 复制代码
	<a href={% url 'app01:index_name' %}>app01应用</a>
	<a href={% url 'app02:index_name' %}>app02应用</a>

reverse解析也可以这样找到某个主路由下面的子路由别名,获取其路由规则

python 复制代码
	reverse('app01:index_name')

解决方式2:别名不能冲突(加上自己应用名作为前缀),在子路由内定义

python 复制代码
	# app01>urls.py
	path('login/', views.login,name='app01_index_name')
	
	# app02>urls.py
	path('index/', views.index,name='app02_index_name')

但是更推荐使用方式一,因为每次只需要加上名称空间作为前缀就可以找到其下面对应的子路由别名。更主要的是:少写很多字符

虚拟环境

什么是虚拟环境?

python 复制代码
	项目1需要使用:django1.11 								 python38
	项目2需要使用:django2.22 pymysql requests			      python38
	项目3需要使用:django3.22 request_html flask urllib3		 python38

	虚拟环境:能够针对相同版本的解释器创建多个分身 每个分身可以有自己独立的环境
	pycharm创建虚拟环境:(每创建一个虚拟环境就相当于重新下载了一个全新的解释器)

​ 实际开发过程,我们需要给不同的项目配备不同的环境,项目需要用到的环境是什么我们就给装什么样的环境。一般不用的我们不装,因为虚拟环境创建太多(第三方模块或者工具太),是会消耗硬盘空间。

​ 目前我们不使用虚拟环境,所有的模块统一下载到本地

如何创建虚拟环境?

终端命令

python 复制代码
	1.创建一个虚拟环境
	python -m venv pyvenv38  # 只能是python
	2.激活
	activate(cd scripts进入文件内删)
	3.关闭
	deactivate

注意:python命令此处不支持多版本共存的操作 python27 python36 python38

pycharm创建虚拟环境

python 复制代码
	1、创建虚拟环境
	2、虚拟环境标志
	3、虚拟环境下载django
	4、使用虚拟环境
	
	如果下载过程出现问题了,就复制提示的解决方法到文件上方框里去执行
	pip install --index-url http://mirrors.aliyun.com/pypi/simple/ django==1.11.11 --trusted-host mirrors.aliyun.com

Django1.x与2.x版本的区别

python 复制代码
	1. 路由文件
		django1.x中使用的是url:支持正则
		django2.x中使用的是path(不支持正则:精准匹配)和re_path(url):支持正则
		path:但是它只支持五种转换器
	
	
	Django默认支持以下5个转化器:
	
	● str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
	● int,匹配正整数,包含0。
	● slug,匹配字母、数字以及横杠、下划线组成的字符串。
	● uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
	● path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
	
	自定义转化器
	2. 创建表关系
		django2中必须指定参数:on_delete:
	    djanxo1中不用指定:级联更新级联删除
相关推荐
笨鸟先飞,勤能补拙2 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能3 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂4 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study4 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境5 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境5 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1235 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102166 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb006 小时前
一个项目带你入门AI应用开发01
python
0566466 小时前
Python康复训练——常用标准库
开发语言·python·学习