框架第四课---路由分发,名称空间,虚拟环境,视图层之必会三板斧与JsonResponse对象源码分析,视图层之request对象获取文件,视图层之FBV与CBV,CBV源码剖析,模板语法过滤器

昨日内容回顾

  • 数据增删改查可视化界面

    1.request对象方法
      request.GET
      request.POST
      request.method
    2.数据库迁移命令
      python38 manage.py makemigrations
      python38 manage.py migrate
    3.form表单提交数据的特征
      action参数:三种情况\method参数:两种情况
    
  • django请求生命周期流程图

cd pythonProject # 先切到你想创django文件夹的目录下
django-admin startproject 项目名 # 创建django项目文件夹

python38 manage.py runserver 指定用哪个python解释器启动django项目 cmd命令

客户端浏览器
web服务网关接口
django中间件
路由层
视图层
模板层
模型层
数据库
  • 路由层之路由匹配

    APPEND_SLASH = True
    # 当路由匹配失败后,会自动帮用户输入的路由后面加斜杠,再次发送请求匹配路由
    
    path('index/', views.index)  # 固定死的
    index(request对象)
    
  • 路由层之路由转换器

    path('index/<str:name>/<int:id>/', view.index)  # 动态匹配
    index(request对象,name=匹配到的内容 ,id=匹配到的内容 )
    # 前两斜杠间的数据转字符串传给name,后两斜杠间的数据转整型传给id,最后一起最为参数传给index函数执行!!!
    
    默认提供五种转换器并且支持自定义转换器(自己写正则)
    
  • 路由层之正则匹配

    re_path('^index/$', views.index)  # 固定死的
    index(request对象)
    
  • 路由层之无名有名分组

    re_path('^index/\d+/.*?/',views.index)  # 不分组的情况
    index(request对象)
    
    re_path('^index/(\d+)/(.*?)/',views.index)  # 无名分组
    index(request对象, \d+匹配到的内容, .*?匹配到的内容)
    
    re_path('^index/(?P<year>\d+)/(?P<id>.*?)/',views.index)  # 有名分组
    index(request对象,year=匹配到的内容,id=匹配到的内容)
    ?P<year>就是给匹配到的内容起个名字year,起名?P<year>不影响正则匹配的!!!
    
  • 反向解析

    根据一个名字解析出一个结果!!!!!!!!
    该结果可以访问某一个对应的路由!
    
    路由固定写死的情况下,我们有需求要改路由,html页面与后端怎么动态解析?
    前端页面与后端跳转要用到的对应路由不想手动改的情况:
    
    path('index/', views.index, name='index_view')
    <a href='/index/'></a>
    
    <a href='{% url 'index_view' %}'></a> 	/index/
    reverse('{% url 'index_view' %}')		/index/
    


路由固定动态匹配的情况下,我们有需求要改路由,html页面与后端怎么动态解析?
path('index/str:info/', views.index, name='index_view')

{% url 'index_view' 'jason' %}  	 /index/jason/
reverse('index_view',args=('tony',))	 /index/tony/


re_path('index/(?P\d+)/', views.index, name='index_view')

{% url 'index_view' '321' %} 		/index/321/
reverse('index_view',args=('111',))		/index/111/

> # 今日内容概要

* 路由分发
* 名称空间
* 虚拟环境
* 视图层之必会三板斧
* 视图层之JsonResponse对象
* 视图层之request对象获取文件!!!!!!!!!
* 视图层之FBV与CBV
* CBV源码剖析

> # 今日内容详细

.
.
.
### 路由分发

```python
路由分发就是做应用的整合!!!
比如一个大项目需要很多人,每个人写一个应用,每个人写的应用里面,各自有自己的路由与视图函数的对应关系 当每个人的应用都写好后。

这个时候作为领导只需要把自己建一个空的django项目,把每个人写好的应用代码全部拷贝进来,在settings里面注册一下,然后在项目的总路由里面做一个路由的分发,每个人写的应用就全部都连到一起去了!!

---------------------------------------------------------------------
---------------------------------------------------------------------
django支持每个应用都可以有自己独立的路由层、静态文件、模板层。基于该特性多人开发项目就可以完全解耦合,之后利用路由分发还可以整合到一起

多个应用都有很多路由与视图函数的对应关系 这个时候可以拆分到各自的路由层中

使用路由分发之前 总路由直接干路由与视图函数的匹配

  path('index/', index)

.

使用路由分发之后 总路由只按照应用名分配匹配方向

django项目下的urls.py文件里面的 只需要做一个路由分发就行了
总路由不再做路由与视图函数的对应关系了!!
只做一个接待或者引路的活!!!
当用户输入网址后先在路由分发这筛选一下,用户到底是想去哪个应用下访问内容,然后顺便将路由转到对应的应用下的urls文件里面去!找对应的视图函数!!!

	path('app01/', include('app01.urls'))
	path('app02/', include('app02.urls'))
------------------------------------------------------
应用文件里面的urls.py文件里面的路由与视图函数关系
比如app01应用里面路由与视图函数关系:
urlpatterns = [path('index/', views.index_func),]

app02应用里面路由与视图函数关系:
urlpatterns = [path('index/', views.index_func),]

.
.
.

名称空间

路由分发之后,针对相同的别名能否自动反向解析出不同的应用前缀???
	默认情况下是无法直接识别应用前缀的
----------------------------------------------------------
----------------------------------------------------------
如果想要正常识别区分有两种方式
方式1:名称空间在总路由里面,提前声明名称空间namespace:
	总路由
    path('app01/', include(('app01.urls', 'app01'), namespace='app01')),
    path('app02/', include(('app02.urls', 'app02'), namespace='app02')),
	反向解析
    reverse('app01:index_view')
    reverse('app02:index_view')
----------------------------------------------------------
方式2:别名不冲突即可!!!   用这个简单
	将应用名作为别名的前缀!!!
	就可以避免多个应用别名冲突的情况!!!
	path('index/', views.index, name='app01_index_view')
	path('index/', views.index, name='app02_index_view')
	反向解析
    reverse('app01_index_view')
    reverse('app02_index_view')
----------------------------------------------------------
这时候反向解析就正常了!!!否则反向解析别名的时候会出问题,无法直接识别应用前缀,导致解析的都是一个路由
----------------------------------------------------------

.
.
.

虚拟环境 重要!!!

不同的项目可能需要不同的模块!!!比如:
项目1需要使用的模块:django1.11         python38
项目2需要使用的模块:django2.22 pymysql requests      python38
项目3需要使用的模块:django3.22 request_html flask urllib3     python38
------------------------------------------------------
要求:
实际开发项目中我们只会给项目配备所需的环境,不需要的一概不配!!!
因为我们下载的多余的模块是要消耗一定资源的!!!目的就是为了节省资源!!!
所以我们的需求是针对不同的项目,配备独有无二环境!!!
-------------------------------------------------------

虚拟环境

虚拟环境:能够针对相同版本的解释器创建多个分身,每个分身可以有自己独立的环境
------------------------------------------------------
pycharm创建虚拟环境:(每创建一个虚拟环境就相当于重新下载了一个全新的解释器)
命令行的方式: python -m venv pyvenv38
------------------------------------------------------
注意:python命令此处不支持多版本共存的操作 python27 python36 python38
要用哪个python解释器,就要把哪个解释器的路径,提到环境变量的最上面!!!
------------------------------------------------------
要先cd切换到虚拟环境的项目的script文件里面,在输入命令:
    激活
        activate
    关闭
        deactivate
------------------------------------------------------
换源下载模块的时候有时候可能需要在命令后面加一行--trusted-host mirrors.aliyun.com
告诉系统该源是可信任的!!!
pip install --index-url http://mirrors.aliyun.com/pypi/simple/ django==1.11.11 --trusted-host mirrors.aliyun.com

image
虚拟环境的解释器
image
.
可以看到此时虚拟环境的解释器里面是几乎什么模块都没有的!!纯净模式!!
现在在该虚拟环境下就可以安装其他的模块,来实现不同项目不同环境的需求了!!!
image
.
.
.
.

视图层之必会三板斧

用来处理请求的视图函数都必须返回HttpResponse对象-----完全正确!!!
diango视图函数里面返回值不用三板斧必报错    HttpResponse,render,redirect
查看3个的源码发现    HttpResponse是一个类   render是一个函数

---------------------------------------------
class HttpResponse:
    pass
----------------------------------------------
def render():
    return HttpResponse()
----------------------------------------------
def redirect():
    redirect_class = 类(祖先有个类是HttpResponse)
    return redirect_class()
----------------------------------------------

HttpResponse 是一个类 HttpResponse('xxx') 产生一个对象
image
.
render 是一个函数,
但是该函数的返回值是HttpResponse(content, content_type, status)产生的还是一个对象
image
.
erdirect 是一个函数,默认permanent=False 所以三元表达式走后面的HttpResponseRedirect
所以return redirect_class() 就是return HttpResponseRedirect()
image
.
HttpResponseRedirect查看源码,最后看到继承的还是HttpResponse这个类
image
.
.
.
.

JsonResponse对象 重要!!!

# 返回给浏览器一个json格式的字符串
不用JsonResponse的情况
def index_func(request):
    user_dict = {'name': 'jason老师', 'age': 18}
    import json
    user_json = json.dumps(user_dict, ensure_ascii=False)
    return HttpResponse(user_json)

ensure_ascii=False  作用是取消在序列化的时候给中文编码,不然在页面上显示的时候就会将文字变成乱码了!!!
------------------------------------------------------
from django.http import JsonResponse
def index_func(request):
    user_dict = {'name': 'jason老师', 'age': 18}
    return JsonResponse(user_dict)
------------------------------------------------------
------------------------------------------------------
ps:以后写代码很多时候可能需要参考源码及所学知识扩展功能,见下面的图
最终版,解除中文乱码
from django.http import JsonResponse
def index_func(request):
    user_dict = {'name': 'jason老师', 'age': 18}
    return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':false})
------------------------------------------------------
from django.http import JsonResponse
def index_func(request):
    l1 = [11,22,33,44,55]
    return JsonResponse(l1,json_dumps_params={'ensure_ascii':false},safe=False)

JsonResponse主要序列化字典 针对非字典的其他可以被序列化的数据需要修改safe参数为False
------------------------------------------------------

image
这个时候中文是乱码的!!!
image
.
底层还是用的json.dumps(user_dict)
image
.
中文乱码问题解决:
如果参数是none 令参数等于一个空字典 字典前面加** 就是将字典打散成关键字参数传到函数里面去!!!
image
image
image
.
序列化列表时报错!!! 要再加个参数 safe=False
image
image
.
.
.
.
.

视图层之request对象获取文件 重要!!!!

form表单携带文件类型的数据需要做到以下几点
	1.method 必须是post   !!!
	2.enctype 必须是multipart/form-data  !!!

表单标签中设置enctype="multipart/form-data"来确保匿名上载文件的正确编码。
只有使用了multipart/form-data,才能完整的传递文件数据!!!
django后端需要通过request.FILES获取文件类型的数据
-------------------------------------------
def index_func(request):
    if request.method == 'POST':
        print(request.POST)  # 获取普通的数据
        print(request.FILES)  # 获取字典数据!!{键:[文件对象]}
        file_obj = request.FILES.get('file')    # 直接拿到文件对象了
        print(file_obj.name)
        with open(r'%s'%file_obj.name,'wb') as f:
            for line in file_obj:    # 文件对象支持for循环一行一行的读取内容!!
                f.write(line)
    return render(request,'index_page.html')
----------------------------------------------
def index_func(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('file')
        print(file_obj.name)
        with open(r'%s'%file_obj.name,'wb') as f:
            for line in file_obj:
                f.write(line)
    return render(request,'index_page.html')
-----------------------------------------------
<body>
    <h1>获取用户数据</h1>
    <form action="" method="post" enctype="multipart/form-data">
# 不加enctype="multipart/form-data" 后端只能拿到的就只能是文件名!!!
        <p>username:
            <input type="text">
        </p>
        <p>hobby:
            <input type="checkbox" name="hobby" value="basketball">篮球
            <input type="checkbox" name="hobby" value="football">足球
            <input type="checkbox" name="hobby" value="dcball">双色球
        </p>
        <p>file:
            <input type="file" name="file">
        </p>
        <input type="submit" value="选我">
        <button>点我</button>
    </form>
</body>

image
request.POST 只能拿到文件的名字!!
image
.
image
image
.
.
.
.

视图层之FBV与CBV

---------------------------------------------------
FBV		基于函数的视图

	def index(request):return HttpResponse对象
---------------------------------------------------
---------------------------------------------------
CBV		基于类的视图比基于函数的视图 更加的灵活
在类里面只需要写跟请求方法相同的函数名,当路由触发了该类的视图函数的运行时,会根据请求方式的不同,自动触发对应的函数名函数的运行!!!函数的视图它是要在函数体代码里面自己加if判断请求方式是什么,然后才能去执行对应的函数!!!

会自动根据请求方法的不同自动匹配对应的方法并执行!!!

cbv视图层代码:
from django import views
	class MyLoginView(views.View):
        def get(self, request):
            return HttpResponse('from CBV get function')

        def post(self, request):
            return HttpResponse('from CBV post function')


views.View 是个类!!!
------------------
cbv的路由层代码:
	path('login/', views.MyLoginView.as_view())

# 为什么要在类名的后面接.as_view()  见后面剖析

.
.
.

CBV源码剖析(重要!!!)

-------------------------------------------------------------

1.从CBV的路由匹配切入
	path('login/', views.MyLoginView.as_view())
  1.类名点名字(名字的查找问题,先自身,再到类里找,再到父类里找!!)
  2.类名点名字并加括号调用(两种情况静态方法staticmethod、绑定给类的方法classmethod)
-------------------------------------------------------------

2.函数名加括号执行优先级最高 项目一启动就会自动执行as_view方法
	path('login/', views.Myloginview.as_view())     变成了
	path('login/', view)  # CBV路由本质还是FBV
-------------------------------------------------------------

3.浏览器地址栏访问login路由需要执行view函数!!!
view() 执行view函数

	对象调用dispatch方法(注意查找顺序先自己的,再到产生对象的类里面,最后再到父类里面找!!一定要记住!!!!!!!!!!)
-------------------------------------------------------------

4.研究父类中的dispatch方法
	获取当前请求方法并转小写
	之后利用反射获取类中对应的方法并执行

dispatch 发货,派遣,分派(v)
-------------------------------------------------------------

class View:
     @classmethod
     def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            return self.dispatch(request, *args, **kwargs)

 self.dispatch() 这个地方注意:
查找顺序先自己的,再到产生对象的类里面,最后再到父类里面找
现在我们用的是父类里面的dispatch方法  如果在我们自己的类里面定义一个dispatch方法
那么这个地方self.dispatch()就运行自己类里面的dispatch方法!!!
后面经常在该出变换花招!!!
---------------------------

     def dispatch(self, request, *args, **kwargs):
         handler = getattr(self, request.method.lower())
         return handler(request, *args, **kwargs)

image
.
as_view 是绑定给类的方法!!
image
image
image
.

所以cls就是我们自己写的类Myloginview
所以self = cls(**initkwargs)  所以cls() 就是产生我们自己写的类的对象

image
.
获取当前请求方法并转小写,并判断在不在默认的8个方法里
image
image
.
getattr(obj,'str') 根据字符串获取对象名称空间的对应的属性名的值或函数名!!!
利用反射方法getattr拿到我们自己类里面的get方法的函数名!!!用handler接收
最后handler()运行函数,这样就实现了request.method拿到什么什么请求,最终就会执行我们自己定义的类里面对应的函数!!!
image
.
.
.

模板层

"""
{{ }}:主要与数据值相关
{% %}:主要与逻辑相关

django的模板语法是自己写的 跟jinja2不一样

1.针对需要加括号调用的名字, django模板语法会自动加括号调用你只需要写名字就行
2.模板语法的注释前端浏览器是无法查看的 {##}
3.
"""
------------------------------------------------------------
1.模板语法传值给模板层方法
def index(request)
    name='jason'
    age=18
    return render(request, 'demo02.html', {'n1': name, 'a1': age})
html页面  {{n1}}   {{a1}}       这样拿值
# 传值方式1:精准传值 不浪费资源 针对多资源的传递书写麻烦

    return render(request,'demo02.html', locals())
html页面  {{name}}   {{age}}     这样拿值
# 传值方式2:将函数名称空间中所有的名字全部传递 名字过多并且不使用的情况下比较浪费资源
------------------------------------------------------------
2.模板语法传值特性
    1.基本数据类型正常展示
    2.文件对象也可以展示并调用方法
    3.函数名会自动加括号执行并将返回值展示到页面上(不支持额外传参)
    4.类名也会自动加括号调用
    5.对象则不会
    6.模板语法的注释,前端是识别不出来的,因为页面要先经过后端代码的处理,然后再发送给前端,经过后端代码的处理后,模板语法的注释直接就被去掉了,所以前端是看不到后端的模板语法的注释的!!
ps:总结针对可以加括号调用的名字,模板语法都会自动加括号调用!!!
--------------------------------------------------------------
3.模板语法之过滤器(内置函数)
--------------------------------------------------------------

1.基本数据类型正常展示
image
image
image
.
.
2.文件对象也可以展示并可以调用文件对象的方法,不需要加括号,django会自动帮你加括号运行
image
image
image
image
.
.
3.当在html页面上使用后端传过来函数名时,函数名会自动加括号执行,并将返回值展示到页面上(不支持额外传参)
image
image
image
.
.
4.当在html页面上使用后端传过来类名时,类名也会自动加括号调用,对象加括号就无所谓了,还是原来的对象,还可以使用对象去点类里面的函数
image
image
image
.
image
image
.
.
.

模板语法之过滤器(相当于是内置函数)

对从后端传过到html文件上的数据,提供了统一的操作方式!!!!
html页面:

<p>{{i|add:10}}</p>     把后端的数据i加10  再展示出来
<p>{{s|add:'baby'}}</p>  把后端的数据s与字符串拼接后,再展示出来

<p>{{l|length}}</p>
把后端的数据l统计一下长度,列表字符串数据,都支持被统计,再展示出来

<p>{{s|slice:'1:4'}}</p>   对字符串切片后展示

<p>{{s|truncatechars:3}}</p>   截选3个字符展示

<p>{{s|truncatewords:3}}</p>   截选3个单词展示

<p>{{ctime|date:'Y-m-d H:i:s'}}</p>   展示时间年月日时分秒

<p>{{i|filesizeformat}}</p>    将数字按1024转成字节数

<p>{{h1|safe}}</p>    可以将传过来的标签,正常展示

image
.
add的底层源码,先尝试这把两个数据转整型再相加,如果在转换的过程中出错了,就干脆就让这两个数据直接拼接,如果还不行就返回一个空!!!
比如如果是两个数字,就可以直接加,如果是两个字符串就可以用+号拼接
如果一个是字符串一个是数字那就两个都执行不了就只能返回一个空!!
image
.
python <p>{{s|truncatechars:3}}</p> 截选3个字符展示
image
.
python <p>{{s|truncatewords:3}}</p> 截选3个单词展示
image
.
.
image
image
image
.
.
image
image
image
.
.
image
image
.
.
image
image
image
.
.
.
.

作业

1.整理今日内容及博客
2.全面熟悉各部分代码
3.预习明日内容(模板层、模型层)
posted @ 2022-12-13 16:26  tengyifan  阅读(59)  评论(0)    收藏  举报