django请求生命周期流程图
django的生命周期是从用户发送HTTP请求数据到网站响应的过程。 整个过程的流程包括: 浏览器发送HTTP请求 ——> wsgiref服务 ——> 中间件 ——> 路由层urls ——> 视图层views ——> models模型层 ——> (通过orm)MySQL获取数据 ——> 视图层 ——>templates模板层
——> 视图层渲染 ——> 中间件 ——> 响应内容给浏览器 1.首先,用户在浏览器中输入一个url,发送一个GET方法的request请求。 2.在django中有一个封装了socket的方法模块wsgiref,监听端口接受request请求,初步封装传送到中间件。 3.由中间件传输到路由系统中进行路由分发,匹配对应的视图函数。 4.将request请求传输到views视图函数中进行逻辑处理。 5.调用models中表对象,通过orm操作数据库拿到数据,同时去templates中相应的模板进行渲染 6.用response响应传输到中间件,依次处理,响应给浏览器展示给用户
- Django自带的web服务网管接口是wsgiref,因为他能承受的并发量特别低,因此我们在项目中会换成uwsgi,它的并发量比wsgiref高很多。
- WSGI与wsgiref和uwsgi之间的关系是:WSGI是协议,wsgiref和uwsgi是实现协议功能的模块。
- 中间件可以看成保安,数据的进出都会经过他的检查。
- 路由层就是存放网址后缀与视图之间的关系。
- 视图层存放功能函数,通过函数调用模版层中的html网页,同时在函数内通过models.py建立与数据库的交互。
django路由层
什么是路由
- 路由你可以看成就是除去IP和的PORT之后的地址
- 本质是URL与要为该URL调用的视图函数之间的映射表,如果把网站比喻为一本书,那路由就好比是这本书的目录。
- 在Django中路由默认配置在urls.py中
路由匹配
路由匹配的结构: path('admin/', admin.site.urls)
参数说明:
django1.X第一个参数是正则表达式,django2.X及以上 path第一个参数写什么就匹配什么
第二个参数是函数名/类名)
# 一旦网址后缀匹配上了就会自动执行后面的函数,并且结束整个路由的匹配(后面再有相同的网址后缀,也不会再匹配了)
ps:无论什么版本django都自带加斜杠后缀的功能,也可以取消。
当我们在用网址访问网页的时候,有些网址需要在末尾加上‘/’符号,有些则不用,但是这些不用‘/’结尾的网址在跳转后,末尾自动补了一个‘/’。我们在配置文件中修改APPEND_SLASH 配置就可以设置这个功能,当他的值为True,就会自动添加斜杠进行匹配,如果改成False就不会自动匹配。
APPEND_SLASH = False # 加上这个配置不会自动添加
不建议添加,用户体验不好
转换器
正常情况下很多网站都会有很多相似的网址,如果我们每一个都单独开设路由不合理。当网址后缀不固定的时候,可以使用转换器来匹配。
path转换器五种类型(只在django2.x版本以上才有)
str:匹配除路径分隔符外的任何非空字符串。 int:匹配0或者任意正整数。 slug:匹配任意一个由字母或数字组成的字符串。 uuid:匹配格式化后的UUID。 path:能够匹配完整的URL路径 ps:还支持自定义转换器(自己写正则表达式匹配更加细化的内容)
匹配格式:将对应位置匹配到的数据转换成固定的数据类型
path('index/<str:info>/', views.index_func),
# index_func(实参request对象,info='转换器匹配到的类型转换之后的内容') path('index/<str:info>/<int:id>/', views.index_func)
# index_func(实参request对象,info='转换器匹配到的类型转换之后的内容',id='转换器匹配到的类型转换之后的内容')
re_path正则匹配(很重要)
# re_path的结构: re_path(正则表达式,函数名)
特点:后缀只要有正则表达式符合的文本,那么就算匹配成功,不管后缀后面有多少内容! 可以在后缀后面加上斜杠(也可以区分跟路由匹配的结果)
re_path('^test/$', views.test) # 为了提高匹配的精准度,可以在正则表达式里开头加^结尾加上$,这样匹配的文本就必须和输入的保持一致
PS:django1.X路由匹配使用的是url() 功能与django2.X及以上的re_path()一致
正则匹配的无名有名分组
什么是分组、为何要分组呢?
比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号),可以使用 http://127.0.0.1:8001/article/?id=3,也可以直接将参数放到路径中http://127.0.0.1:8001/article/3/
针对后一种方式Django就需要直接从路径中取出参数,这就用到了正则表达式的分组功能了,分组分为两种:无名分组与有名分组
无名分组
将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数
re_path('^test/(\d{4})/', views.test) re_path('^test/(\d{4})/(.*?)/', views.test) # 第一个网址后缀只能是test,第二个后缀是可以匹配任意4位数字,第三个后缀是可以匹配任意类型任意数量
匹配成功的分组部分会以位置参数的形式传给视图函数,有几个分组就传几个位置参数。
有名分组
- 将正则表达式分组捕获到的内容定义一个名字,即起别名
- 会将括号内正则表达式匹配到的内容当做key=value关键字参数传递给后面的视图函数
url(r'^testadd4/(?P<id>\d+)/$',views.testadd4) """ 有名分组 将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数 """
ps:注意上述的有名分组和无名分组不能混合使用!!!
反向解析
反向解析应用的场景:页面上写了很多固定的路由,一旦路由发生改变,那么所有相关页面的链接都会失效 为了防止这个问题,我们需要使用反向解析。
应用范围
- 模板中的超链接
- 视图中的重定向
如何使用反向解析
1. 给路由与视图函数对应关系添加一个别名(名字自己指定,只要不冲突即可)
# 路由层 url(r'^index/',views.index,name='index_name')
2. 根据该别名动态解析出一个结果,该结果可以直接访问到对应的路由
前端使用(模板层) {% url '你给路由与视图函数对应关系起的别名' %} <a href="{% url 'index_name' %}">111</a>
后端使用(视图层) reverse('你给路由与视图函数对应关系起的别名') from django.shortcuts import reverse reverse('index_name')
ps:redirect括号内也可以直接写别名
无名分组反向解析
- 路由层配置
url(r'^index/(\d+)/',views.index,name='index_name') # 路由层中分组匹配得到的数字并不是我们这样写死的, 一般情况下放的是数据的主键值, 我们可以通过获取到数据的主键.进而定位到数据对象, 从而可以对数据进行编辑和删除
- 后端配置(视图层)
后端 reverse('index_name',args=(1,)) # 只要给个数字即可
- 前端配置(模板层)
前端 <a href="{% url 'index_name' 1 %}"></a> # 只要给个数字即可
有名分组反向解析
- 路由层配置
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中的每一个应用都可以有自己的urls.py,static文件夹,templates文件夹,基于上述特点,使用django做分组开发非常的简便
每个人只需要写自己的应用即可,最后由组长统一汇总到一个空的django项目中然后使用路由分发将多个应用关联到一起
- 利用路由分发之后, Django每一个app下面都可以有自己的urls.py路由层,templates文件夹,static文件夹。
- 项目名下 urls.py(总路由)不再做路由与视图函数的匹配关系而是做路由的分发。进而识别当前url所属的应用, 最后直接分发给对应的应用去处理就行了, 并且应用路由重名也无关要紧
准备工作
1. 新建app02,app03
python3 manage.py startapp app02 python3 manage.py startapp app03
2. 然后在settings.py文件注册
#settings.py文件 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'app02', 'app03' ]
3. 手动创建子路由urls.py
文件和templates
总路由分发设置
- 总路由:项目下urls.py文件
from django.conf.urls import url, include from django.contrib import admin # 复杂版本 from app01 import urls as app01_urls from app02 import urls as app02_urls from app03 import urls as app03_urls urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/', include(app01_urls)), url(r'^app02/', include(app02_urls)), url(r'^app03/', include(app03_urls)), ] # 进阶版本 urlpatterns = [ url(r'^app01/', include('app01.urls')), url(r'^app02/', include('app02.urls')), url(r'^app03/', include('app03.urls')), ]
注意:
- 总路由正则后面不能添加
"$"
, 不然一配到app01
就结束了 - 每个应用路由(子路由)文件 : urls.py
# 在应用下新建urls.py文件,在该文件内写路由与视图函数的对应关系即可 from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/',views.index) ]
名称空间
定义:
当一个项目创建了多个应用并设置了多个别名,当多个应用在反向解析的时候如果出现了别名冲突的情况,反向解析无法自动识别前缀,并且反向解析的时候前面路由会被后面的路由覆盖, 那么就无法触发前面路由对应的视图函数, 为了避免这种错误, 引入了名称空间。
作用:
- URL命名空间可以保证反查到唯一的URL,即使不同的app使用相同的URL名称。
- 它还允许你在一个应用有多个实例部署的情况下反向解析URL。
- 换句话讲,因为一个应用的多个实例共享相同的命名URL,命名空间提供了一种区分这些命名URL 的方法。
应用命名空间与实例命名空间
- name_app : 应用命名空间,通常在应用app的urls.py文件中指定 (Django2版本以后不指定报错)
#app01 app_name ='app01' # 应用命名空间 urlpatterna = [ re_path(r'^'index',views.index,name='name_index') ]
- namespace : 实例命名空间 : 通常在总路由文件中指定
解决方式一:使用名称空间
- 总路由设置:urls.py文件
from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/', include('app01.urls', namespace='app01')), url(r'^app02/', include('app02.urls', namespace='app02')), url(r'^app03/', include('app03.urls', namespace='app03')) ]
- 子路由设置:urls.py文件
#app01 from app01 import views from django.conf.urls import url urlpatterns = [ url(r'^index', views.index, name="index_name"), url(r'^login', views.login, ) ] #app02 from app02 import views from django.conf.urls import url urlpatterns = [ url(r'^index', views.index, name='index_name'), url(r'^login', views.login, ) ] #app03 from app02 import views from django.conf.urls import url urlpatterns = [ url(r'^index', views.index, name='index_name'), url(r'^login', views.login, ) ]
- 视图文件:views.py文件
#app01 from django.shortcuts import render, HttpResponse, reverse # Create your views here. def index(request): return HttpResponse("from app01 index") def login(request): reverse("app01:index_name") return HttpResponse('from app01 login')
#app02 from django.shortcuts import render, HttpResponse, reverse # Create your views here. def index(request): return HttpResponse("from app02 index") def login(request): reverse("app02:index_name") return HttpResponse('from app02 login')
#app03 from django.shortcuts import render, HttpResponse, reverse # Create your views here. def index(request): return HttpResponse("from app03 index") def login(request): reverse("app03:index_name") return HttpResponse('from app03 login')
- 模板层文件
<a href="{% url 'app01:index_name' %}"></a> <a href="{% url 'app02:index_name' %}"></a> <a href="{% url 'app03:name_index' %}"></a>
解决方式二:别名不能冲突(手动加上自己应用名作为前缀)
url(r'^index/',views.index,name='app01_index_name') url(r'^index/',views.index,name='app02_index_name') url(r'^index/',views.index,name='app03_index_name')
虚拟环境
1.虚拟环境的作用
- 在时间开发过程中,我们会给不同的项目配备不同的环境
- 项目用到什么就装什么,用不到的一概不装
- 不同的项目解释器环境都不一样
2. 虚拟环境说明
- 创建虚拟环境类似于你重新下载了一个纯净的python解释器
- 如果反复创建类似于反复下载,会消耗一定的硬盘空间
- ps:我们目前不推荐你使用虚拟环境,所有的模块统一全部下载到本地
3. 创建虚拟环境
pycharm创建:
- 打开pycharm----->File----->New Project----->Pure Python
命令行创建:
python -m venv pyvenv38 # 注意:python命令此处不支持多版本共存的操作 python27 python36 python38
- 创建成功查看初始的 package : 打开Pycharm----->File----->settings----->Project:[项目名]----->Python Interpreter
- 下次创建新项目可以直接选择已经创建好的虚拟环境