目录
- Django基础之视图层(views.py)和模板层
- 视图层
- JsonResponse对象
- form表单上传文件
- FBV与CBV 即CBV源码分析
- django settings源码分析及实际应用
- 模板层
- 模板传值
- 过滤器( | )
- 常用内置过滤器
- 其他过滤器(了解)
- 标签
- if判断
- for循环
- with标签
- csrf_token标签
- 自定义过滤器和标签
- 自定义过滤器
- 自定义标签
- 自定义过滤器和标签的区别
- 模板的继承
- 模板的导入
Django基础之视图层(views.py)和模板层
视图层
JsonResponse对象
首先提一下,之前讲到过小白必会三板斧(重要三对象):HttpResponse、render、redirect,要注意的是django视图函数必须要返回一个HttpResponse对象。
render内部工作原理
from django.template import Template,Context # 用到两个模块
def index(request):
res = Template(" {{ user }}
")
con = Context({'user':{'username':'jason','pwd':'123'}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)
用来返回json格式数据的为什么要给前端返回json格式字符串?
因为涉及到一个前后端分离的概念。
前后端分离就是基于json格式传输数据,后端就专门写接口,前端调用你这个接口,就能够拿到一个json格式的字符串,然后前端利用序列化反序列化转换成前端对应的数据类型。
json常用数据类型
数值类型
字符类型
数组 []
自定义对象 {}
undefined与null
布尔值 true false
symbol
前端的序列化 : JSON.stringify() >>>>>>>> json.dumps()
前端的反序列化:JSON.parse() >>>>>>>>> json.loads()
eg:
from django.http import JsonResponse
def index(request):
data = {'name':'jason好帅哦 我好喜欢','password':123}
l = [1,2,3,4,5,6,7,8]
# 后端朝前端返回json格式的字符串方式一
# res = json.dumps(data,ensure_ascii=False) # 把参数ensure_ascii=False就不会把中文转成json格式
# return HttpResponse(res)
# 方式二
# return JsonResponse(data,json_dumps_params={'ensure_ascii':False}) # 让参数json_dumps_params={'ensure_ascii':False}作用也是不把中文转成json格式
return JsonResponse(l,safe=False) # JsonResponse默认只支持序列化字典,如果你想序列化其他类型(json能够支持的类型),需要将safe参数由默认的True改为False。
form表单上传文件
注意事项:
enctype属性需要由默认的urlencoded变成formdata
method属性需要由默认的get变成post
(目前还需要考虑的是 提交post请求需要将配置文件中的csrf中间件注释)
如果form表单上传文件 后端需要在request.FILES获取文件数据 而不再是POST里面
eg:
urls.py
from django.conf.urls import url
from django.contrib import admin
from homework import views
urlpatterns = [
url(r'^file/$', views.file)
]
views.py
from django.shortcuts import render, HttpResponse, redirect, reverse
def file(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: # file_obj可以直接看成文件句柄f
# f.write(line)
# 也可以用下面一种方法
for chunk in file_obj.chunks():
f.write(chunk)
return render(request, 'file.html')
在templates目录下新建file.html
Title
{% load static %}
FBV与CBV 即CBV源码分析
FBV(Function Based View):基于函数的视图
CBV(Class Based View):基于类的视图
CBV和FBV在路由匹配上,规则都是一样的,都是路由后面跟的函数的内存地址
在类中写了两个方法 一个叫get一个叫post
为什么前端get请求来就会触发get方法
post请求来就会触发post方法 如何实现的???
这就讲一下CBV源码的实现思路
1、CBV路由层
url(r'^reg/',views.MyReg.as_view()) # 在这必须调用自己写的MyReg类中的as_view方法
# as_view的源码
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls就是我们自己的写的MyReg类
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 上面的一通操作 就是给我们自己写的类的对象赋值
return self.dispatch(request, *args, **kwargs)
# 对象在查找属性或方法的时候 顺序是什么? 先从自己找 再从产生对象的类中找 再去类的父类中找...
"""也就意味着你在看源码的时候 你一定要牢记上面的话"""
return view
2、视图层views.py中写上继承view的类
from django.views import View
class MyReg(View):
def get(self,request):
return render(request,'reg.html')
def post(self,request):
return HttpResponse("我是MyReg类中post方法")
3、在前端发送请求的时候就会进到CBV源码的dispatch方法判断请求方式在不在默认的八个请求方式中。
"""CBV源码最精髓的部分"""
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names: # 判断当前请求方式在不在默认的八个请求方式中
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
# handler = getattr(自己写的类产生的对象,'小写的请求方法(get\post)','获取不到对应的方法就报错')
# handler就是我们自己定义的跟请求方法相对应的方法的函数内存地址
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 再调用获取到的方法
django settings源码分析及实际应用
django的配置文件有两个
一个是暴露给用户可以自定义配置的
一个是默认的全局配置文件
用户指定了就用用户的
用户没有指定就用默认的
from django.conf import settings
settings = LazySettings()
class LazySettings(LazyObject):
def _setup(self, name=None):
# os.environ你可以把它看成是一个全局的大字典
settings_module = os.environ.get(ENVIRONMENT_VARIABLE) # 从大字典中取值
# settings_module = 'day59.settings'
self._wrapped = Settings(settings_module) # Settings('day59.settings')
class Settings(object):
def __init__(self, settings_module): # settings_module = 'day59.settings'
for setting in dir(global_settings): # 循环获取global_settings文件中所有的名字
if setting.isupper(): # 在判断名字是否是大写
# 如果是大写 利用反射 获取到大写的名字所对应的值 不停地添加到对象中
setattr(self, setting, getattr(global_settings, setting))
# store the settings module in case someone later cares
self.SETTINGS_MODULE = settings_module
mod = importlib.import_module(self.SETTINGS_MODULE) # 'day59.settings'
# from day59 import settings
# mod 指代的就是暴露给用户的配置文件模块名
for setting in dir(mod): # 循环获取暴露给用户配置文件中所有的名字
if setting.isupper(): # 判断是否是大写
setting_value = getattr(mod, setting) # 如果是大写 获取大写的变量名所对应的值
setattr(self, setting, setting_value) # 不停的给对象设置值
模板层
模板传值
传函数名的时候,会自动加括号调用函数,将函数的返回值展示在html页面上,django模板语法不支持函数传参。
django模板语法在获取容器类型内部元素的值得时候,统一只采用句点符(.)
句点符既可以引用容器类型的元素,也可以引用对象的方法,如下
test.html
Title
{{ msg.upper }}
{{ dic.k1 }}
{{ obj.name }}
{{ li.1.upper }}
{{ li.2.age }}
例如给html页面传了一个列表l,那在前端取值就用 {{ l.1 }}
,就用列表.索引取值。
过滤器( | )
1、优点类似于小的方法,特点:会将|左边的当做过滤器的第一个参数,将
右边的当做过滤器第二个参数。
过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,具体语法如下
{{ 变量名|过滤器名:传给过滤器的参数 }}
常用内置过滤器
# 1、default
#作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing”
{{ value|default:"nothing" }}
#2、length
#作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4
{{ value|length }}
#3、filesizeformat
#作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是 12312312321,输出将会是 11.5 GB
{{ value|filesizeformat }}
#4、date
#作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}
#5、slice
#作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg"
{{ value|slice:"0:2" }}
#6、truncatechars
#作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}
#7、truncatewords
#作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world egon 嘎嘎“,则输出"hello world ..."
{{ value|truncatewords:2 }}
#8、safe
#作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="",模板变量{{ value }}会被渲染成交给浏览器后会被解析成普通字符”“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语法意义,那么就用到了过滤器safe,比如value='点我啊',在被safe过滤器处理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’点我啊‘
{{ value|safe }}
内置过滤器safe用来前后端取消转义(前端代码并不一定非要在前端写,也可以在后端写好,传递给前端页面)
前端取消转义:|safe
后端取消转义:要先导一个模块:
from django.utils.safestring import mark_safe
sss = "渡我不渡她"
res = mark_safe(sss)
# 再把这个res传到前端
{{ res }}
# 前端就能识别a标签了
# 9、add
给value加上一个add后面的数值
eg:给前端传个n=123,`{{ n|add:100 }}`这个过滤器结果就是n+100,在页面上显示的结果就是223.
其他过滤器(了解)
过滤器 | 描述 | 示例 |
---|---|---|
upper | 以大写方式输出 | {{ user.name | upper }} |
add | 给value加上一个数值 | {{ user.age | add:”5” }} |
addslashes | 单引号加上转义号 | |
capfirst | 第一个字母大写 | {{ ‘good’| capfirst }} 返回”Good” |
center | 输出指定长度的字符串,把变量居中 | {{ “abcd”| center:”50” }} |
cut | 删除指定字符串 | {{ “You are not a Englishman” | cut:”not” }} |
date | 格式化日期 | |
default | 如果值不存在,则使用默认值代替 | {{ value | default:”(N/A)” }} |
default_if_none | 如果值为None, 则使用默认值代替 | |
dictsort | 按某字段排序,变量必须是一个dictionary | {% for moment in moments | dictsort:”id” %} |
dictsortreversed | 按某字段倒序排序,变量必须是dictionary | |
divisibleby | 判断是否可以被数字整除 | `{{ 224 | divisibleby:2 }} 返回 True` |
escape | 按HTML转义,比如将”<”转换为”<” | |
filesizeformat | 增加数字的可读性,转换结果为13KB,89MB,3Bytes等 | `{{ 1024 | filesizeformat }} 返回 1.0KB` |
first | 返回列表的第1个元素,变量必须是一个列表 | |
floatformat | 转换为指定精度的小数,默认保留1位小数 | {{ 3.1415926 | floatformat:3 }} 返回 3.142 四舍五入 |
get_digit | 从个位数开始截取指定位置的数字 | {{ 123456 | get_digit:’1’}} |
join | 用指定分隔符连接列表 | {{ [‘abc’,’45’] | join:’*’ }} 返回 abc*45 |
length | 返回列表中元素的个数或字符串长度 | |
length_is | 检查列表,字符串长度是否符合指定的值 | {{ ‘hello’| length_is:’3’ }} |
linebreaks | 用或 标签包裹变量 |
{{ “Hi\n\nDavid”|linebreaks }} 返回HiDavid |
linebreaksbr | 用 标签代替换行符 |
|
linenumbers | 为变量中的每一行加上行号 | |
ljust | 输出指定长度的字符串,变量左对齐 | {{‘ab’|ljust:5}}返回 ‘ab ’ |
lower | 字符串变小写 | |
make_list | 将字符串转换为列表 | |
pluralize | 根据数字确定是否输出英文复数符号 | |
random | 返回列表的随机一项 | |
removetags | 删除字符串中指定的HTML标记 | {{value | removetags: “h1 h2”}} |
rjust | 输出指定长度的字符串,变量右对齐 | |
slice | 切片操作, 返回列表 | {{[3,9,1] | slice:’:2’}} 返回 [3,9] `{{ 'asdikfjhihgie' | slice:':5' }} 返回 ‘asdik’` |
slugify | 在字符串中留下减号和下划线,其它符号删除,空格用减号替换 | `{{ '5-2=3and5 2=3' | slugify }} 返回 5-23and5-23` |
stringformat | 字符串格式化,语法同python | |
time | 返回日期的时间部分 | |
timesince | 以“到现在为止过了多长时间”显示时间变量 | 结果可能为 45days, 3 hours |
timeuntil | 以“从现在开始到时间变量”还有多长时间显示时间变量 | |
title | 每个单词首字母大写 | |
truncatewords | 将字符串转换为省略表达方式 | `{{ 'This is a pen' | truncatewords:2 }}返回``This is ...` |
truncatewords_html | 同上,但保留其中的HTML标签 | `{{ ' This is a pen ' | truncatewords:2 }}返回``This is ... ` |
urlencode | 将字符串中的特殊字符转换为url兼容表达方式 | {{ ‘http://www.aaa.com/foo?a=b&b=c’ | urlencode}} |
urlize | 将变量字符串中的url由纯文本变为链接 | |
wordcount | 返回变量字符串中的单词数 | |
yesno | 将布尔变量转换为字符串yes, no 或maybe | `{{ True | yesno }}{{ False | yesno }}{{ None | yesno }} ``返回 ``yes``no ``maybe` |
标签
if判断,for循环、with标签、csrf_token标签等。
if判断
eg:
{% if xo %}
xo有值
{% else %}
xo没有值
{% endif %}
{% if num > 100 or num < 0 %}
无效
{% elif num > 80 and num < 100 %}
优秀
{% else %}
凑活吧
{% endif %}
#3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
for循环
#1、遍历每一个元素:
{% for person in person_list %}
{{ person.name }}
{% endfor %}
#2、可以利用{% for obj in list reversed %}反向循环。
#3、遍历一个字典:
{% for key,val in dic.items %}
{{ key }}:{{ val }}
{% endfor %}
{% for key in dic.keys %}
{{ key }}
{% endfor %}
{% for val in dic.values %}
{{ val }}
{% endfor %}
#4、循环序号可以通过{{ forloop }}显示
forloop.counter 当前循环的索引值(从1开始)
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是第一次循环则返回True,否则返回False
forloop.last 当前循环是最后一次循环则返回True,否则返回False
forloop.parentloop 本层循环的外层循环
#5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句
{% for person in person_list %}
{{ person.name }}
{% empty %}
sorry,no person here
{% endfor %}
eg:
urls.py
from django.urls import url
from app01 import views
urlpatterns = [
url(r'^test/',views.test)
]
view.py
def test(request):
names=['egon','kevin']
dic={'name':'egon','age':18,'sex':'male'}
list1=[]
return render(request,'test.html',locals())
test.html
Title
{% for name in names %}
{{ forloop.counter0 }} {{ name }}
{% endfor %}
{% for name in names reversed %}
{{ forloop.revcounter0 }} {{ name }}
{% endfor %}
{% for k,v in dic.items %}
{{ forloop.counter }} {{ k }} {{ v }}
{% endfor %}
{% for item in list1 %}
{{ item }}
{% empty %}
sorry,no value here
{% endfor %}
with标签
# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值
{% with li.1.upper as v %}
{{ v }}
{% endwith %}
csrf_token标签
# 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求
# 具体工作原理为:
# 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端生成的一串随机字符串,如
# 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份
自定义过滤器和标签
步骤:
在应用名下面新建一个templatetags文件夹(必须叫这个名字)
在该文件夹下,新建一个任意名称的py文件
在该py文件内,固定先写两行代码:
from django.template import Library
register = Library()
自定义过滤器
后端my_tag.py
from django.template import Library
register = Library()
@register.filter(name='myplus')
def index(a, b):
return a+b
前端index.html
{% load my_tag %}
{{ 123|myplus:123 }}
这样前端页面显示的结果就是246
自定义标签
my_tag.py
from django.template import Library
register = Library()
@register.simple_tag(name='mysm')
def login(a, b, c, d):
return '%s/%s/%/%s'%(a, b, c, d)
login.html
{% load my_tag %}
{% mysm 1 2 3 4 %}
结果是1/2/3/4
自定义过滤器和标签的区别
#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数
#2、过滤器可以用于if判断,而标签不能
{% if salary|my_multi_filter:12 > 200 %}
优秀
{% else %}
垃圾
{% endif %}
模板的继承
extends标签和block标签
实现需要在要继承的模板中,通过block划定区域
{% block 区域名字 %}
{% endblock %}
子模板中如何使用
# include有的功能extends全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容
{% extends '想要继承的模板' %}
{% block 区域名字 %}
登录页面
{% endblock %}
一个页面上block块越多,页面的扩展性越高,通常情况下,都应该有三片区域
{% block content %}
{% endblock %}
{% block css %}
{% endblock %}
{% block js %}
{% endblock %}
eg:
Django模版引擎中最复杂且最强大的部分就是模版继承了。我们以先创建一个基本的“骨架”模版,它包含我们站点中的全部元素,并且可以定义多处blocks ,例如我们创建base.html内容如下
{% block title %}自定义title名{% endblock %}
模板base.html 定义了一个可以用于两列排版页面的简单HTML骨架。我们新建子模板index.html的主要工作就是继承base.html然后填充/覆盖其内部的blocks。
{% extends "base.html" %}
{% block title %}
index页面
{% endblock %}
{% block sidebar %}
{{ block.super }}
拍卖
金融
{% endblock %}
{% block content %}
index页面内容
{% endblock %}
我们通过django访问index.html看到内容如下(block标签的内容都完成了替换或更新)
index页面
总结与注意
#1、标签extends必须放在首行,base.html中block越多可定制性越强
#2、include仅仅只是完全引用其他模板文件,而extends却可以搭配block在引用的基础上进行扩写
#3、子模板中变量{{ block.super }} 可以重用父类的内容,然后在父类基础上增加新内容,而不是完全覆盖
#4、为了提升可读性,我们可以给标签{% endblock %} 起一个名字 。例如:
{% block content %}
...
{% endblock content %}
#5、在一个模版中不能出现重名的block标签。
模板的导入
当你写了一个特别好看的form表单,想在多个页面上都使用这个form表单,你就可以将你写的form表单当做模块的形式导入,导入过来之后,就可以直接展示。
模板导入之include标签,作用:在一个模板文件中,引入/重用另外一个模板文件的内容
语法:{% include '要导入的模板名称' %}
、
eg:
可以把广告栏写到专门的文件里advertise.html
Panel title
Panel content
Panel title
Panel content
Panel title
Panel content
然后在base.html文件中用include标签引入advertise.html文件的内容
Title
{% include "advertise.html" %}