Django视图层与模板层
视图层
JsonResponse
向前端返回一个json格式字符串的两种方式
方式一:
import json
def my_view(request):
data = ['tank', 'sean', 'jason']
return HttpResponse(json.dumps(data))
方式二:
from django.http import JsonResponse
def my_view(request):
data = ['tank', 'sean', 'jason']
return JsonResponse(data, safe=False)
# 默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
form表单上传文件
前端
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit">
</form>
后端
def homes(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.chunks():
f.write(line)
return render(request, 'home.html')
render原理
需要导入两个模块Template,Context
from django.template import Template,Context
def ab_render(request):
temp = Template("<h1>{{ user_dic }}{{ user_dic.username }}{{ user_dic.password }}</h1>")
user_dic = Context({'user_dic': {'username': 'jason', 'password': '123'}})
res = temp.render(user_dic)
return HttpResponse(res)
FBV和CBV
Django的视图层由两种形式构成:FBV和CBV
- FBV(Function base view)基于函数的的视图,之前一直都是FBV
- CBV(Class base view)基于类的视图
CBV的基本写法
urls.py
from app01 import views
urlpatterns = [
# CBV路由配置
url(r'^login', views.MyLogin.as_view()),
# 必须调用类下的方法as_view
]
views.py
class MyLogin(View):
def get(self, request):
return render(request, 'login.html')
def post(self, request):
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'egon' and pwd == '123':
res = '登录成功'
else:
res = '用户名或密码错误'
return HttpResponse(res)
login.html
<form action="" method="post">
<p>username:<input type="text" name="name"></p>
<p>password:<input type="text" name="pwd"></p>
<input type="submit">
</form>
login提交get请求会自动执行MyLogin中的get方法,提交post请求会自动执行MyLogin中的post方法,为什么能够针对不同的请求能够自动执行对应的方法
CBV的原理
在对应关系中:url(r'^login', views.MyLogin.as_view())
as_view
-
要么是类中定义的方法(普通函数)
@staticmethod
-
要么是类中定义的绑定类的方法
@classmethod
看源码得知,是绑定给类的方法
Django配置文件原理
Django暴露给用户一个可以自定义的配置,但是内部也有默认的配置
from django.conf import global_settings, settings
点进settings可以看到
settings = LazySettings() # 这是个基于模块的单例
# 而 LazySettings()很明显是调用了一个类或函数
点进LazySettings可以看到
针对在哪里设置了"DJANGO_SETTINGS_MODULE"
这个键值对,点进manage.py
然后在点进LazySettings类中的Settings可以看到
这里有个小方法importlib
有两个py文件,a和b
b在文件夹conf下,写有name = 'jason'
在a中想要获取这个名字可以有两种方法
第一种
from conf import b
print(b.name) # jason
第二种
import importlib
res = 'conf.b'
md = importlib.import_module(res)
# 该方法最小单位是模块,不能是模块里面的单个名字
print(md.name) # jason
有助于以后看源码
模板层
模版语法符号:
{{ }} 变量相关
{% %} 逻辑相关
模版语法之传值取值
Python基本数据类型全部支持传给HTML页面(函数和类也可以传)
函数和对象会自动加括号调用,但模板语法不支持传参
后端给HTML页面传值的两种方式
- 指名道姓的传
# 方式一
return render(request, 'index.html', {'n': 11, 'f': 11.11, 's': 'hello world'})
- locals()
# 方式二 locals会将当前名称空间中所有的变量名全部传给HTML页面
return render(request, 'index.html', locals())
HTML页面中,如何获取到后端传递过来的数据
{{ 变量名 }} 比如:{{ n }} {{ f }} {{ s }}
取值
Django模版语法取值,使用句点符,索引,键
<p>{{ l.2 }}</p>
<p>{{ d.username }}</p>
<p>{{ d.password }}</p>
模版语法之过滤器(变量相关)
过滤器一个 | ,管道符
左边的会当做过滤器的第一个参数 | 过滤器右边的会当做第二个参数
|length 求数据长度
{{ s|length }} 11
|add 加法运算
{{ n|add:10 }} {{ s|add:'DSB' }} 21 hello worldDSB
|default 默认值(判断值是否为空)
{{ b|default:'这个b布尔值是True' }} True
|truncatechars 截取字符
{{ s|truncatechars:8 }} hello...
|truncatewords 截取单词
{{ s|truncatewords:8 }} hello world
|filesizeformat 文件大小
{{ file_size|filesizeformat }} 2.8G
|slice 切片操作
{{ s|slice:'0:2' }}、{{ s|slice:"0:8:2" }} he、hlow
|date 日期格式化
{{ ddd|date:'Y-m-d' }} 2020-01-07
|safe 转义
{{ res|safe }} hello(一级标签hello)
转义在后端也可以实现
from django.utils.safestring import mark_safe
res2 = mark_safe("<h1>hello</h1>")
{{ res2 }} hello(一级标签hello)
总结:前端代码不一定非要在前端页面写,可以在后端写好传递给前端页面使用,这样就可以利用到后端更多的逻辑语法
模板语法之标签(逻辑相关)
{% %}
for 循环和if判断
{% for foo in l %}
{% if forloop.first %}
<p>这是第一次</p>
{% elif forloop.last %}
<p>这是最后一次</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% empty %}
<p>for循环的对象内部没有值</p>
{% endfor %}
前端的for循环,对于字典的内置函数,它也有
{% for foo in d.keys %}
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.values %}
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.items %}
<p>{{ foo }}</p>
{% endfor %}
当一个值获取的步骤非常繁琐,但是又需要在很多地方需要用到,我们可以起别名
{% with d.hobby.1.username.1 as eg %}
<p>{{ eg }}</p> <!--只能在with体内用-->>
{% endwith %}
自定义过滤器、标签和inclusion_tag
先完成以下前期准备工作
- 在应用名下新建一个名字必须叫templatetags文件夹
- 在该文件夹内新建一个任意名称的py文件(eg:mytag)
- 在该文件内 必须先写以下两句代码
from django.template import Library
register = Library()
自定义过滤器
# 自定义过滤器
@register.filter(name='my_sum')
def index(a, b):
return a + b
<p>自定义过滤器的使用</p>
{% load mytag %}
<p>{{ 10|my_sum:90 }}</p>
100
<p>自定义的过滤器可以在逻辑语句中使用而自定义标签不可以</p>
{% if 10|my_sum:100 %}
<p>条件成立</p>
{% endif %}
自定义标签
# 自定义标签
@register.simple_tag(name='my_baby')
def xxx(a, b, c, d):
return "%s?%s?%s?%s" % (a, b, c, d)
<p>自定义标签的使用</p>
{% load mytag %}
<p>{% my_baby 1 2 3 "hello world" %}</p>
1?2?3?hello world
自定义inclusion_tag
# 自定义inclusion_tag
@register.inclusion_tag('demo.html', name='myinc')
def index1(n):
l = []
for i in range(n):
l.append(i)
# 将列表传递给demo.html
return {'data': l}
<p>自定义inclusion_tag的使用</p>
{% load mytag %}
{% myinc 7 %}
<!--demo.html-->
<ul>
{% for foo in data %}
<li>{{ foo }}</li>
{% endfor %}
</ul>
总结:页面上使用他们,统一先导入load
{% load mytag %}
模版的继承
某一个页面大部分区域都是公用的,那这个页面就可以作为模版页面,当别人继承这个页面之后,如何修改对应的区域
先在模版页面上通过block实现划定区域
{% block content %}
{% endblock %}
子页面中先导入整个模版
{% extends '模版页面.html' %}
修改特定的区域,通过实现规定好的区域名称
{% block content %}
子页面内容
{% endblock %}
通常情况下,模版页面应该有三块区域
{% block css %}
模板页面内容
{% endblock %}
{% block content %}
模板页面内容
{% endblock %}
{% block js %}
模板页面内容
{% endblock %}
例子:
urls.py
# 模版的导入
url(r'^mdzz/', views.mdzz),
url(r'^loginn/', views.loginn),
url(r'^reg/', 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, 'reg.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>
{% load static %}
<link rel="stylesheet" href="{% static '/bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static '/bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</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 class="row">
<div class="col-md-3">
<div class="list-group">
<a href="/mdzz" class="list-group-item active">
首页
</a>
<a href="/reg" class="list-group-item">注册</a>
<a href="/loginn" class="list-group-item">登录</a>
<a href="#" class="list-group-item">修改密码</a>
<a href="#" class="list-group-item">注销</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>
</body>
</html>
loginn.html
{% extends 'mdzz.html' %}
{% 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>
<input type="submit" class="btn btn-success" value="登录">
</form>
{{ block.super }}
{% endblock %}
reg.html
{% extends 'mdzz.html' %}
{% 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>
<input type="submit" class="btn btn-info" value="注册">
</form>
{{ block.super }}
{% endblock %}
模块的block块越多,可扩展性越高
还支持子页面调用父页面对应区域的内容,并且可以无限次调用
{{ block.super }}
模版的导入
将HTML页面当做模块使用,哪里需要导哪里,这个HTML页面通常不是完整的,只是一0个局部样式
{% include 'left.html' %}
基于django settings源码实现项目配置文件的 插拔式设计
自己实现一个类似于Django配置文件
用户可管理的配置文件(settings)
NAME = '我是暴露给用户的配置文件'
项目默认的配置文件(global_settings)
NAME = '我是项目默认的配置文件'
启动文件
import os, 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)
__init__.py
import importlib, os
from lib.conf import global_settings
class Settings(object):
def __init__(self):
# 先循环遍历项目默认的全局配置文件
for name in dir(global_settings):
# 判断变量名是否是大写
if name.isupper():
# 将键值对设置给对象
k = name # NAME
v = getattr(global_settings, name)
setattr(self, k, v)
# 先获取暴露给用户的配置文件的字符串路径
module_path = os.environ.get('xxx')
# 利用importlib模块导入settings文件
md = importlib.import_module(module_path)
# 同上
for name in dir(md):
# 判断变量名是否是大写
if name.isupper():
# 将键值对设置给对象
k = name # NAME
v = getattr(md, name)
setattr(self, k, v)
settings = Settings()