Django的路由层
一、orm表关系如何建立
多对多
一对多
一对一
换位思考,先站在一张表上面分析,然后再站在另一张分析。
二、django请求生命周期流程图
Django框架将这个工作过程细分为如下四层去实现
1、路由层(根据不同的地址执行不同的视图函数)
2、视图层(定义处理业务逻辑的视图函数)
3、模型层 (跟数据库打交道的)
4、模板层(待返回给浏览器的html文件)
三、urls.py —— 路由层
1. 路由匹配
# 在 urls.py 文件中,有一个urlpatterns列表。
# 其中就放各种路由分发路径。
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls), # url第一个参数是一个正则表达式
url(r'^test/(\d+)/', views.test), # 一旦正则表达式能够匹配到内容 会立刻结束匹配关系 直接执行后面对应的函数
url(r'^testadd/(?P<year>\d+)/', views.testadd),
]
规律:
不加斜杠,先匹配一次试试。
如果匹配不上,会让浏览器重定向,加一个斜杠再来一次。
如果还匹配不上,才会报错。
注意:
- 路由匹配只匹配url部分
- 不匹配?后面的get携带的参数
# 取消django自动让浏览器加斜杠的功能
APPEND_SLASH = False # 该参数默认是True
2. 正则分组匹配
利用有名和无名分组,我们就可以在调用视图函数之前,捕获到浏览器访问服务器携带的一些数据,并将这些数据额外传递给函数。
有名分组和无名分组不能混合使用!!
但是同一种分组可以使用多次。
2.1 无名分组
无名分组:将urls内的正则表达式的无名分组匹配到的内容当做位置参数传递给视图函数。
urls的路径中,正则表达式捕获到数据分成一组,并按照组之间的先后顺序,给它们命名为1,2,3,4这样的名字,这是无名分组。
并将分组内的第一个数据当做位置参数传给对应的视图函数。
这个时候视图函数的形参就不只需要一个request了,还需要另一个形参用来接收正则分组捕获到的数据,这个形参由于是用来接收位置参数的,因此可以随便命名。
def test(request,xxx):
print('多余的参数:',xxx)
return HttpResponse('test view')
2.2 有名分组
有名分组:将urls内的正则表达式的有名分组匹配到的内容当做关键字参数传递给视图函数。
urls的路径中,正则表达式捕获到数据分成一组,并给这组数据一个自己取的名字,这是有名分组。
并将分组内的第一个数据当做关键字参数传给对应的视图函数。
这个时候视图函数的形参就不只需要一个request了,还需要另一个形参用来接收正则分组捕获到的数据,这个形参由于是用来接收关键字参数的,因此需要和有名分组的组名一致。
# urls
url(r'^testadd/(?P<year>\d+)/', views.testadd),
# views
def testadd(request,year):
print('多余的关键字参数:',year)
return HttpResponse('testadd view')
3. 反向解析
根据一个别名,解析出一个动态结果,该结果(即一个地址)可以用来拼接想要访问的url。
可以通过给一个动态url设置一个viewname,用来记录这个动态变化的url,前后端可以通过解析这个viewname,来获取变化着的动态url并直接访问或者拼接访问。
解析出来的url如果不是我需要直接访问的地址,那我需要在解析出来的url后面拼接一些分组正则后缀,这时候该怎么做呢?
由于url动态变化的方式不相同(位置实参,关键字实参),可以分为以下三种情况讨论:
3.1 路由中没有正则
# urls
url(r'^home/', views.home,name='xxx'), # 这个xxx就是viewname
前端反向解析:
<!-- 对应的前端页面 -->
<p><a href="{% url 'xxx' %}">111</a></p>
<!-- href="/home/" -->
后端反向解析:
from django.shortcuts import reverse
# 反向解析
url = reverse('xxx')
3.2 路由中含有无名分组正则
# urls
url(r'^home/(\d+)/', views.home,name='xxx'),
前端反向解析:
前端获取到动态url后,发现url需要一个正则后缀,所以需要在反向解析前就手动指定你需要访问的url的正则匹配的内容是什么。
<!-- 对应的前端页面 -->
<p><a href="{% url 'xxx' 1234 %}">111</a></p>
<!-- href="/home/1234/" -->
<!-- 数字通常是数据的主键值 -->
<!-- 编辑功能和删除功能 -->
后端反向解析:
from django.shortcuts import reverse
# 手动传入的参数 只需要满足能够被正则表达式匹配到即可
# args 为关键字,里面放参数元组
url = reverse('xxx',args=(1234,)) # 里面是元祖,所以要加逗号
3.3 路由中含有有名分组正则
# urls
url(r'^home/(?P<year>\d+)/', views.home,name='xxx'),
前端反向解析:
<!-- 对应的前端页面 -->
<p><a href="{% url 'xxx' year=1232 %}">111</a></p>
<!-- 也可以直接用无名分组的语法,上下两句等价 -->
<p><a href="{% url 'xxx' 1232 %}">111</a></p>
后端反向解析:
from django.shortcuts import reverse
# 再反向解析函数中传入关键字参数
url = reverse('xxx',kwargs={'year':1234})
# 或者也可以直接用无名分组的方法,上下两句相等
url = reverse('xxx',args=(1234,))
4. 路由分发
前提:
在django中所有的app都可以有自己独立的urls.py;templates;static。
正是由于上面的特点,我们在用django开发项目就能够做到多人分组开发,互相不干扰,每个人只开发自己的app。
小组长只需要将所有人开发的app整合到一个空的django项目里面,然后在settings配置文件注册。再利用路由分发将多个app整合到一起即可完成大项目的拼接。
上述情况会导致一个问题:当一个项目较大,其中含有多个app,导致总匹配关系过多的时候,urls.py文件就会变得很冗杂,就会导致结构不清晰,不便于管理。
为了解决这个问题,我们应该将app自己的路由交由自己管理,然后在总路由表中做分发。
因此,我们需要使用路由分发来解决这个问题。
使用路由分发,将会使总路由不再做匹配的活。
而仅仅是做任务分发(请求来了之后,总路由不做对应关系。只询问你要访问哪个app的功能,然后将请求转发给对应的app去处理。)
# 总路由 -方法一
from django.conf.urls import include
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
url(r'^admin/', admin.site.urls), # url第一个参数是一个正则表达式
# 路由分发
url(r'^app01/',include(app01_urls)), # 路由分发需要注意的实现 就是总路由里面不能以$结尾
url(r'^app02/',include(app02_urls)),
]
# 总路由 -方法二
# 最省事的写法(******)
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app02.urls'))
# 子路由1
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('^reg/',views.reg)
]
# 子路由2
from django.conf.urls import url
from app02 import views
urlpatterns = [
url('^reg/',views.reg)
]
5. 名称空间
当我们的项目下创建了多个app,并且每个app下都针对匹配的路径起了别名,如果别名存在重复,那么在反向解析时则会出现覆盖。
这时你就可以在做路由分发的时候,给每一个app创建一个名称空间,然后在反向解析的时候,选择到底去哪个名称空间中查找别名。
# urls
url(r'^app01/',include('app01.urls',namespace='app01')),
url(r'^app02/',include('app02.urls',namespace='app02'))
# 后端
print(reverse('app01:reg'))
print(reverse('app02:reg'))
# 前端
<a href="{% url 'app01:reg' %}"></a>
<a href="{% url 'app02:reg' %}"></a>
其实上面的名称空间知识点可以完全不用,你只需要保证起别名的时候,在整个django项目中不冲突即可。
# 参考建议
# 起别名的时候统一加上应用名前缀
urlpatterns = [
url(r'^reg/',views.reg,name='app02_reg')
]
urlpatterns = [
url('^reg/',views.reg,name='app01_reg')
]
6. 伪静态
目的:将一个动态网页伪装成一个静态网页,以此来挺好搜索引擎SEO查询频率和收藏力度。
7. 虚拟环境
目的:给每一个项目,装备该项目所需要的模块,不需要的模块一概不装。
每创建一个虚拟环境就类似于你重新下载了一个纯净python解释器,之后该项目用到上面,你就装什么(虚拟环境一台机器上可以有N多个)。
不要在你的机器上无限制创建虚拟环境。
8. django版本区别
django1.X django2.X
urls.py中路由匹配的方法有区别
# django2.X用的是path
urlpatterns = [
path('admin/', admin.site.urls),
]
# django1.X用的是url
urlpatterns = [
url(r'^reg.html',views.reg,name='app02_reg')
]
区别:django2.X里面path第一个参数不是正则也不支持正则,写什么就匹配什么。
path不支持正则,感觉不好用。不过django2.X还有一个re_path的方法 该方法就相当于django1.X里面url。
虽然path不支持,但是它提供了五种转换器,能够将匹配到的数据自动转换成对应的类型。
除了有默认五种转换器之外 还支持你自定义转换器
四、views.py —— 视图层
form表单上传文件 后端如何获取文件
form表达传文件需要注意的事项
1. method必须改成post
2. enctype该成formdata格式
前期你在使用post朝后端发请求的时候 需要去settings配置文件中注释掉一个中间件csrf。