django 之 视图层、模板层
目录
视图层
一.视图函数返回的都是一个HttpResponse对象
render 和 redirect 都是直接或者间接传的
二.JsonResponse
下面这个是正常的json的序列化
views.py 层 def test1(request): user_dict={'username':'jason 顶天立地 ','password':123} json_str= json.dumps(user_dict,ensure_ascii= False)###保证中文不会转成bytes return HttpResponse(json_str) 浏览器展示的: {"username": "jason 顶天立地 ", "password": 123}
JsonResponse 序列化
from django.http import JsonResponse def test1(request): user_dict={'username':'jason 顶天立地 ','password':123} # json_str= json.dumps(user_dict,ensure_ascii= False) return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})#后面的这个代表的是保证中文不会转成bytes 浏览器展示的: {"username": "jason 顶天立地 ", "password": 123}
如果序列的不是字典样式
def test1(request): # user_dict={'username':'jason 顶天立地 ','password':123} # json_str= json.dumps(user_dict,ensure_ascii= False) l1=[1,'磁场强大','人缘极好'] return JsonResponse(l1,json_dumps_params={'ensure_ascii':False},safe=False)###最后safe为False这个保证非字典 也可以识别 浏览器展示的: [1, "磁场强大", "人缘极好"]
三.form表单的上传
home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.js"></script> </head> <body> <form action="" method="post"enctype="multipart/form-data"> username: <input type="text"name="username"> password: <input type="text" name="password"> <p><input type="file" name="myfile"></p> <input type="submit"> </form> </body> </html>
views.py
def test1(request): if request.method == 'POST': print(request.FILES) file_obj= request.FILES.get('myfile') print(file_obj.name) with open(file_obj.name,'wb') as f: for line in file_obj: f.write(line) ###把图片保存起来 return render(request,'home.html') >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 333.png (image/png)>]}> 333.png FILES可以通过这个拿到文件
注意:form表单上传文件 1.必须做的事 method必须是post enctype必须是formdata 2.暂时需要做的 提交post请求需要将中间件里面的一个csrfmiddleware注释掉 后端如何获取用户上传的文件 file_obj = request.FILES.get('前端input框name属性值') file_obj.name # 文件名 for line in file_obj: print(line) # django中推荐以下写法 for chunk in file_obj.chunks(): print(chunk)
四.ab_render的原理
from django.template import Template,Context def ab_render(request): temp=Template('<h1>{{user_dict}}</h1>') user_dict = Context({'user_dict':{'username':'jason','password':123}}) res= temp.render(user_dict) return HttpResponse(res) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 浏览器显示的: {'username': 'jason', 'password': 123}
五.FBV 和CBV
视图函数并一定就是函数 也可以是类
FBV:基于函数的视图
CBV:基于类的视图
下面是CBV的类的视图
urls
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^ab_render/', views.ab_render), url(r'^login/', views.MyLogin.as_view()), #这个是路由与视图函数对应关系相当于url(r'^login/', views.view) ]
views
from django.views import View class MyLogin(View): def get(self,request): return render(request,'login.html') def post(self,request): return HttpResponse('我是类里面post的方法')
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.js"></script> </head> <body> <form action=""method="post"> <input type="submit"> </form> </body> </html>
遇到上面的这个我们需要考虑一下,并且考虑 一些源码的问题
朝login提交get请求会自动执行MyLogin里面的get方法
而提交post请求也会自动执行MyLogin里面的post方法
为什么MyLogin针对不同的请求方法能够自动执行对应的方法
研究源码的突破口 url(r'^login/',views.MyLogin.as_view()) 猜想 as_view要么是类里面定义的普通函数 @staticmethod 要么是类里面定义的绑定给类的方法 @classmethod 看源码发现是绑定给类的方法
CBV源码(******) MyClass.as_view() # 函数名加括号执行优先级最高 @classonlymethod def as_view(...): def view(...): ... return view # 变形 url(r'^index/',views.view) # CBV与FBV在路由匹配上本质是一样的 def view(...): self = cls(...) # 生成的是我们自己写的类的对象 ... return self.dispatch(...) """ 当你看到self.属性或方法的时候 不要想当然 一定要遵循对象的属性和方法的查询顺序 对象本身 产生对象的类 类的父类 """ def dispatch(...): # 先判断当前请求方式是否在默认的八个合法请求方式内 if request.method.lower() in ['get','post','delete','options'...] # 利用反射获取对象中对应的属性 handler = getattr(self,request.method.lower(),报错信息) return handler(...) # 执行获取到的方法
FBV和 CBV在路由匹配上本质是一样的,都是路由和函数内存地址的对应关系
六.importlib
我们正常的通过模块可以获取模块里面的值
conf包 里面有b.py name= 'david' a.py # from conf import b # print(b.name) >>>>>>>>>>>>>>>>>>>> david
上面的我们可以用其他的importlib来写
conf包 里面有b.py name= 'david' a.py import importlib res= 'conf.b' md=importlib.import_module(res) #该方法的最小单位是模块 不是模块里面的单个名字 print(md.name) >>>>>>>>>>>>>>>>>>>> david
模板层
一.模板传值
python基本数据类型全部支持传递给html文件 函数 类 函数和对象会自动加括号 # 模板语法不支持传参 对象 后端给html文件传递数据的两种方式 1.指名道姓 return render(request,'index.html',{'n':n,'f':f})###放在一个字典里 2.locals() # 会将当前名称空间中所有的变量名全部传递给html页面 return render(request,'index.html',locals()) html页面上 如何获取到后端传递过来的数据 {{ 变量名 }} 取值 django模板语法取值 只有一种操作方式 句点符 . 点索引 点键 <p>{{ l.2 }}</p> <p>{{ d.username }}</p> <p>{{ d.password }}</p> <p>{{ d.hobby.1.username.1 }}</p>
8种数据类型和函数 、类 、对象传递给html文件
views
def index(request): #python的所有的数据类型都支持传递给html页面 n=11 f=11.111 s='hello world' l=[1,2,3,4,5,6] d={'username':'jason','password':123,'hobby':['read',{'username':['egon','jason']}]} t=(1,2,3,4,5,6,) se={1,2,3,4,5} b= True ff= False ss='dsdkljgjsg sjg;d djle dejs;egl dee kd;le;lk ' sss='是的K歌 警方介入进来大家多 罚款交了比较 房间里人家给 外婆' file_size= 3290237579476 def func(): print('func 被执行了') return 'from func' class MyClass(object): def get_self(self): return 'from self' @staticmethod def get_func(): return 'from func' @classmethod def get_cls(cls): return 'from cls' obj = MyClass() return render(request,'index.html',locals())
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> {#<p>{{ n }}</p>#} {#模板语法的注释 这个注释前端浏览器检查是看不见的#} <!--浏览器检查能够看到--> {#<p>{{ f }}</p>#} {#<p>{{ s }}</p>#} {#<p>{{ l }}</p>#} {#<p>{{ d }}</p>#} {#<p>{{ t }}</p>#} {#<p>{{ se }}</p>#} {#<p>{{ b }}</p>#} {#<p>函数名会自动加括号执行 展示的是函数的返回值:{{ func }} 模板语法不支持给函数传参</p>#} {#<p>传类名 也会自动加括号实例化 拿到的是对象地址{{ MyClass }}</p>#} {#<p>{{ MyClass }}</p>#} {#<p>{{ MyClass.get_func }}</p>#} {#<p>{{ MyClass.get_cls }}</p>#} {#<p>{{ MyClass.get_self }}</p>#} {#<p>{{ obj }}</p>#} ##得到的是对象的地址 {#<p>{{ obj.get_func }}</p>#} {#<p>{{ obj.get_self }}</p>#} {#<p>{{ obj.get_cls }}</p>#} {#<p>取值</p>#} {#<p>{{ l.2 }}</p>#} {#<p>{{ d.username }}</p>#} {#<p>{{ d.password }}</p>#} {#<p>{{ d.hobby.1.username.1 }}</p>#} </body> </html>
二.模板的过滤器
如下是具体的语法 |length:求长度 |add:增加值 |default :设置默认值 如果是True 返回True 如果是false 返回默认值 |truncatechars: 截取字符 后面加3个点 |truncatewords:截取单词 后面加3个点 |filesizeformat:文件大小 换算成多少B |slice :切片 |date: 转化成日期格式 |safe:转义 可以把css js等样式渲染到前端
views
def index(request): from datetime import datetime s= 'hello world dvdlej vdnelegjn nej dje vnle vgjnlew ldeg gel eglkkeg ' a= 12 b= True ff = False file_size= 298355976576 ddd= datetime.now() res= '<h1>你好啊</h1>' res1= '<script>alert(123)</script>' return render(request,'index.html',locals())
index.html
{#<p>过滤器 |左边的会当做过滤器的第一个参数 过滤器名右边的会当做过滤器的第二个参数</p>#} {#<p>求数据长度:{{ s|length }}</p>#} {#<p>加法运算:{{ n|add:10 }}、{{ s|add:13132 }}、{{ s|add:'DSB' }}</p>#} {#<p>默认值(判断值是否为空):{{ b|default:'这个b布尔值是True' }}、{{ ff|default:'这个ff布尔值是Flase' }}</p>#} {#<p>截取字符(截取5个字符 三个点也算):{{ s|truncatechars:8 }}</p>#} {#<p>截取单词(截取8个单词 三个点不算):{{ ss|truncatewords:8 }}、{{ sss|truncatewords:4 }}</p>#} {#<p>文件大小:{{ file_size|filesizeformat }}</p>#} {#<p>切片操作:{{ s|slice:'0:2' }}、{{ s|slice:"0:8:2" }}</p>#} {#<p>日期格式化:{{ ddd|date:'Y年/m月/d日' }}</p>#} {#<p>转义:{{ res|safe }}、{{ res1 }}、后端直接标识安全:{{ res2 }}</p>#}
浏览器的显示结果
70 True、2222ff he... hello world dvdlej vdnelegjn nej dje vnle vgjnlew ... 277.9 GB he 2020年/01月/07日 你好啊
前后端取消转义
前端
|safe
后端
from django.utils.safestring import mark_safe
mark_safe(‘
安全滴
‘)总结:前端代码不一定非要在前端页面写,可以在后端写好传递给前端页面使用
这样的话 你就可以利用到后端更加多的逻辑语法
下面是举例
from django.utils.safestring import mark_safe res2= mark_safe('<h1>积极阳光帅气</h1>') return render(request,'index.html',locals()) index.html <p>{{ res2 }}</p>
三.模板语法逻辑相关
forloop 对象
判断for循环的开始结束的位置
索引 计数
如下是在html页面上
{# {% for foo in l %}#} {#{{ forloop }}#} {# {% endfor %}#} 打印的结果是: {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 6, 'revcounter0': 5, 'first': True, 'last': False} {'parentloop': {}, 'counter0': 1, 'counter': 2, 'revcounter': 5, 'revcounter0': 4, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 4, 'revcounter0': 3, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 3, 'counter': 4, 'revcounter': 3, 'revcounter0': 2, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 4, 'counter': 5, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 5, 'counter': 6, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True} ###第一个是True 最后一个是True
for 循环 if 判断
views.py 里面的是 l=[1,2,3,4,5,6] html页面 {% for foo in l %} {% if forloop.first %} <p>这是我的第一次</p> {% elif forloop.last %} <p>这个是最后一次</p> {% else %} <p>{{ foo }}</p> {% endif %} {% endfor %}
浏览器结果
这是我的第一次 2 3 4 5 这个是最后一次
如果是空的 可以用下面的empty来判断
views.py 里面的是 l=[] html页面 {% for foo in l %} {% if forloop.first %} <p>这是我的第一次</p> {% elif forloop.last %} <p>这个是最后一次</p> {% else %} <p>{{ foo }}</p> {% endif %} {% empty %} {# 这个是用来的判断的 如果容器内没有值#} <p>for循环的对象没有值</p> {% endfor %}
四.对于多用和常用的可以起个别名
这个别名只能在with里面使用
views.py def index(request): d = {"username": 'jason', 'password': 123, 'hobby': ['read', {'username': ['jason', 'egon']}]} return render(request,'index.html',{'l':l,'d':d} ) index.html {% with d.hobby.1.username.1 as eg %} <p>别名{{ eg }}</p> {% endwith %}
五.自定义的过滤器、标签 、inclusion_tag
自定义过滤器 标签 inclusion_tag
先完成以下前期准备工作
1.在应用名下新建一个名字必须叫templatetags文件夹
2.在该文件夹内新建一个任意名称的py文件(eg:mytag)
3.在该文件内 必须先写以下两句代码
from django.template import Library
register= Library()
总结 页面上使用他们 统一先导入
{% load mytag %}
mytag.py
from django.template import Library register= Library() #自定义的过滤器 @register.filter(name= 'my_sum') def index(a,b): return a +b #自定义的标签 @register.simple_tag(name= 'my_baby') def index1(a,b,c,d): return '%s?%s?%s?%s?'%(a,b,c,d) #自定义inclusion_tag @register.inclusion_tag('demo.html',name='myin') def index(n): l=[] for i in range(n): l.append(i) #将列表传递给demo.html ## 这个return给demo.html在上面@register这个里面 return {'l':l}
index.html
<body> <p>自定义的过滤器</p> {% load mytag %} <p>{{ 10|my_sum:90 }}</p> {% if 10|my_sum:100 %} <p>条件成立</p> {% endif %} >>>>>>>>>>>>>>>>>>>>>>> 100 <p>自定义的标签</p> {% load mytag %} <p>{% my_baby 1 2 3 'hello world' %}</p> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1?2?3?hello world? <p>自定义的inclusion_tag的使用</p> {% load mytag %} {% myin 5 %} >>>>>>>>>>>>>>>>>>>>>>>>>>>>> 0 1 2 3 4 </body> </html>
demo.html
<ul> {% for foo in l %} <li>{{ foo }}</li> {% endfor %} </ul>
六.模板的继承
? 某一个页面大部分区域都是公用的 那这个页面就可以作为模板页面
? 当别人继承这个页面之后 如何修改对应的区域
先在模板页面上通过block实现划定区域 {% block content %} 模板页面内容 {% endblock %} 子页面中先导入整个模板 {% extends '模板页面.html'%} 修改特定的区域 通过实现划定好的区域名称 {% block content %} 子页面内容 {% endblock %} 通常情况下 模板页面页面应该起码有三块区域 {% block css %} 模板页面内容 {% endblock %} {% block content %} 模板页面内容 {% endblock %} {% block js %} 模板页面内容 {% endblock %} # 模板的block块越多 可扩展性越高 还支持子页面调用父页面对应区域的内容 并且可以无限次调用 {{ block.super }}
urls.py
#模板的导入 url(r'^mdzz/',views.mdzz), url(r'^loginn/',views.loginn), url(r'^register/',views.register)
views.py
def mdzz(request): return render(request,'mdzz.html')def loginn(request): return render(request,'loginn.html')def register(request): return render(request,'register.html')
mdzz.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.js"></script> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Brand</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div calss="row" > <div class="col-md-3" > <div class="list-group"> <a href="/mdzz/" class="list-group-item active">###a标签 首页 </a> <a href="/register/" class="list-group-item">注册</a>###a标签 <a href="/loginn/" class="list-group-item">登录</a>###a标签 <a href="#" class="list-group-item">Porta ac consectetur ac</a> <a href="#" class="list-group-item">Vestibulum at eros</a> </div> </div> <div class="col-md-9"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> {% block content %} <div class="jumbotron"> <h1>Hello, world!</h1> <p>...</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> {% endblock %} </div> </div> </div> </div> </div> {% block js %} {% endblock %} </body> </html>
loggin.html
{% extends 'mdzz.html' %} {% block css %} <style> h2 { color:green; } </style> {% endblock %} {% block content %} {% include 'left.html' %} <h2 class="text-center">登录页面</h2> <form action=""> <p>username: <input type="text" class="form-control"> </p> <p>password: <input type="text" class="form-control" > </p> 提交:<input type="submit " class="btn btn-success"> </form> {{ block.super }} {% endblock %} {% block js %} <script> alert(123) </script> {% endblock %}
register.html
{% extends 'mdzz.html' %} {% block css %} <style> h2{ color:red; } </style> {% endblock %} {% block content %} <h2 class="text-center">注册页面</h2> <form action=""> <p>username: <input type="text" class="form-control"> </p> <p>password: <input type="text" class="form-control"> </p> <p> <input type="submit" class="btn btn-danger"> </p> </form> {{ block.super }} {{ block.super }} {{ block.super }} {% endblock %} {% block js %} <script> alert(666) </script> {% endblock %}
七.模板的导入
将html页面当做模块使用 哪里需要导哪里 这个html页面通常都不是完整的 只是一个局部样式
{% include ‘要导入的模块名‘ %}
举例
left.html
<p>这是我做的很美的一个模块</p>
将left.html模块导入到这个loginn.html中,直接在loginn.htm 要插入的位置放上{% include ‘left.html‘ %} 即可
八.基于django settings源码实现项目配置文件的 插拔式设计
conf.settings.py
NAME='我是暴露给用户的配置文件'
lib.conf.--init--.py
import importlib import os from lib.conf import global_settings class Settings(object): def __init__(self): #先遍历出项目默认的全局匹配值的文件 for name in dir(global_settings): #判断变量名是否大写 if name.isupper(): #键值对设置对象 k = name v= getattr(global_settings,name) setattr(self,k,v) #先获取暴露给用户的配置文件的字符串路径 module_path = os.environ.get('xxx') #里面的importlib模块 导入到settings文件 md= importlib.import_module(module_path) #md能拿到settings里面的所有的名字 #同上操作 for name in dir(md): #判断变量名是否大写 if name.isupper(): #键值对设置给对象 k= name v= getattr(md,name) setattr(self,k,v) settings = Settings()
lib.conf.global_settings.py
NAME ='我是项目默认的配置文件'
start.py
import os import sys BASE_DIR = os.path.dirname(__file__) sys.path.append(BASE_DIR) if __name__ == '__main__': #项目启动 就应该朝全局大字典中设置键值对 os.environ['xxx']= 'conf.settings' from lib.conf import settings print(settings.NAME)
暴露给用户的配置文件,如果用户的存在 就用用户的, 如果用户的没有 ,就是用全局系统默认的
django其实有两个配置文件 一个是暴露给用户的可以自定义的配置 一个是项目默认的配置 用户如果配置了就用用户的 没有配置就用默认的 from django.conf import global_settings,settings settings = LazySettings() class LazySettings(...): def _setup(...): # 获取暴露给用户的配置文件字符串路径 setting_module = os.environ.get(纯大写变量名) """ manage.py os.environ.setdefault(纯大写变量名,'暴露给用户的配置文件字符串路径') """ Settings(setting_module) def Settings(...) # 先遍历全局默认的配置文件 给对象设置键值对 for setting in dir(global_settings): if setting.isupper(): setattr(self,setting,getattr(global_settings,setting)) # 再遍历暴露给用户的配置文件 给对象设置键值对 md = importlib.import_module(setting_module) for setting in dir(md): if setting.isupper(): setattr(self,setting,getattr(md,setting)) """ 利用的其实就是字典的键存在和不存在 下面语句的作用 dict[key] = value """