目录
JsonResponse对象
小白必会三板斧: HTTPResponse, render, redirect
视图函数必须要有一个返回值, 并且返回值的
数据类型必须是HTTPResponse对象
render: Template + Context
def test(request): from django.template import Template, Context html_obj = Template('<h1>{{user}}</h1>') data_obj = Context({'user': {'user_name': 'jason'}}) complete_html = html_obj.render(data_obj) return HttpResponse(complete_html)
前后端分离
前后端数据交互采用json, 字符串形式的字典
后端写好相应的url接口, 前端访问这个接口,
后端返回一个大字典给前端, 并附上开发文档
前端根据大字典中的数据和开发文档渲染页面
前后端序列化反序列化使用的方法
- python后端: json.dumps, json.loads
- js前端: JSON.stringify, JSON.parse
设置json不自动对中文转码
方式一: 查看json.dumps源码-->发现ensure_ascii参数, 将其值设为False
def test(request): import json user_dic = {'name': '蔡启龙'} json_str = json.dumps(user_dic, ensure_ascii=False) # {"name": "蔡启龙"} return HttpResponse(json_str) # {"name": "\u8521\u542f\u9f99"}
方式二: 使用django封装的JsonResponse
查看JsonResponse源码-->找到json.dumps-->推测出json_dumps_params参数值
def test(request): from django.http import JsonResponse user_dic = {'name': '蔡启龙'} # {"name": "\u8521\u542f\u9f99"} return JsonResponse(user_dic, json_dumps_params={'ensure_ascii': False}) # {"name": "蔡启龙"}
设置序列化json能序列化的其他数据类型
根据报错信息, 更改参数: safe=False
def test(request): from django.http import JsonResponse lt = [1, 2] return JsonResponse(lt, safe=False) # [1, 2] # In order to allow non-dict objects to be serialized set the safe parameter to False.
CBV及其源码分析
CBV: 基于类的视图, Class Based Views
- FBV: Function Based Views, 基于函数的视图
- FBV的写法: 路由 + 视图函数名,
url(r'^index/', views.index)
- CBV的写法:
url(r'^login/', views.MyLogin.as_view())
, # 返回view, cbv本质上还是fbv, 路由+函数名
CBV的源码, 要求: 能直接说出流程
为什么CBV能够根据请求方式的不同, 自动执行不同的方法
- 函数名加括号执行的优先级最高
- 项目一启动, 就会自动执行as_view方法
- 源码只看自己能看懂的, 不要纠结于看不懂的部分
- 执行类方法as_view(), 返回view
- 正则表达式匹配到"login/", 执行view(),
- view()返回dispatch(request, ...),
- 执行dispatch(request, ...), 会拿到请求方式字符串, 通过反射查找对象中的方法
- 如果找到, 将该方法记录给handler, 找不到则将报错函数记录给handler, 最后返回handler的执行结果, 为一个HttpResponse对象
from django.views import View class MyLogin(View): def get(self, request): print('MyLogin的get方法') return render(request, 'login.html') def post(self, request): return HttpResponse('MyLogin的post方法')
@classonlymethod def as_view(cls, **initkwargs): def view(request, *args, **kwargs): # as_view内的闭包函数 self = cls(**initkwargs) # cls为自定义的MyLogin类, self为自定义的类实例化出的对象 return self.dispatch(request, *args, **kwargs) return view 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) # 以get请求为例, getattr(obj, 'get') else: handler = self.http_method_not_allowed # 调用get方法 return handler(request, *args, **kwargs) # 返回调用get方法的到的结果, 一个HttpResponse对象
给CBV加装饰器的方式
from functools import wraps import time def deco(func): @wraps(func) def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) exec_time = time.time() - start_time print('函数执行时间: %s' % (exec_time,)) return res
方式一: 直接装饰
''' @deco def get(self, request): ... '''
方式二: 使用内置模块, 如果不使用内置模块直接对类装饰会报错: 'function' object has no attribute 'as_view'
from django.utils.decorators import method_decorator ''' @method_decorator(deco, name='post') class MyLogin(View): ... @method_decorator(deco) # 推荐写法 def get(self, request): ... '''
方式三: 一次性给类中所有方法加装饰器, 如果想在视图函数执行之前做一些操作, 可以在CBV中自定义dispatch方法来实现
from django.utils.decorators import method_decorator @method_decorator(deco, name='dispatch') class MyLogin(View): def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
模板语法
模板语法的格式
两种: 变量相关{{}}, 逻辑相关{{%%}}
模板传值
能传的数据类型, python基本的数据类型都支持
def test(request): lt = [1, 2, 3] s = 'hello world' file_len = 666666 tag = "<h1>tag</h1>" user_dic = {'name': 'jason', 'pwd': '123'} def index(): return 'index的函数返回值' class TestClass: pass test_obj = TestClass() # locals会将当前名称空间中的所有名字传递给html页面 return render(request, 'test.html', locals())
传函数名:
<p>{{ index }}</p>
给HTML页面传函数名时, 会自动调用该函数, 并将函数返回值展示出来, 但不支持函数传参
传类名: 自动加括号实例化产生对象, 所有只要是可调用对象, 传递到html页面上都会自动加括号调用
''' <p>{{ TestClass }}</p> <p>{{ test_obj }}</p> <app02.views.test.<locals>.TestClass object at 0x0000028D321F9F28> <app02.views.test.<locals>.TestClass object at 0x0000028D321EAD30> '''
过滤器
模板语法提供的一些内置方法, 用来快速处理数据, 但是最多只能有两个参数
会将|左边的数据当做过滤器的第一个参数传入, ":"右边的数据当做第二个参数传入
统计长度:
<p>{{ lt|length }}</p>
, # 3, 如果无法统计默认返回0加法运算:
<p>{{ s|add:'nick' }}</p>
, # hello worldnick切片操作:
<p>{{ lt|slice:'0:2:1' }}</p>
, # [1, 2], 顾头不顾尾, 也支持步长, 但不支持负数索引转换成文件大小格式:
<p>{{ file_len|filesizeformat }}</p>
, # 651.0 KB截取文本内容:
<p>{{ s|truncatechars:5}}</p>
, # he..., 截取5个字符, 并且3个点也算截取5个单词, 按空格计算, 3个点不算: truncatewords
判断是否有值:
<p>{{ ''|default:'为空时的默认值' }}</p>
, # 为空时的默认值, 非空展示原值, 空展示默认值展示带有标签的文本:
默认情况下不会自动转成前端html标签, 防止恶意攻击
如果要识别html语法, 可以设置在前端或后端设置取消转义
''' 前端设置取消转义 <p>{{ tag }}</p> # <h1>tag</h1> <p>{{ tag|safe }}</p> # tag ''' # 后端设置取消转义 from django.utils.safestring import mark_safe safe_tag = mark_safe(tag) # <p>{{ safe_tag }}</p> # tag
标签
标签指的是与逻辑相关的一系列代码, 例如if判断, for循环等都在标签中
for循环
''' forloop对象: first/last判断for循环的开始与结束 count0, 从0开始的索引值 counter, 对数据进行的计数, 例如解决主键值不连续 {% for num in lt %} <p>{{ forloop }}</p> {% endfor %} {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 3, 'revcounter0': 2, 'first': True, 'last': False} ... '''
if判断
{% if s %} <p>s有值</p> {% else %} <p>s没有值</p> {% endif %}
组合使用
{% for num in lt %} {% if forloop.first %} <p>第一循环, 数字为: {{ num }}</p> {% elif forloop.last %} <p>最后一次循环, 数字为: {{ num }}</p> {% else %} <p>中间循环, 数字为: {{ num }}</p> {% endif %} {% empty %} <p>for循环对象为空时触发</p> {% endfor %}
for循环字典
''' {% for key in user_dic.keys %} <p>{{ key }}</p> {% endfor %} # name pwd {% for value in user_dic.values %} <p>{{ value }}</p> # jason 123 {% endfor %} {% for item in user_dic.items %} <p>{{ item }}</p> # ('name', 'jason') ('pwd', '123') {% endfor %} '''
模板语法的取值
只有一种方式, 统一采用句点符: "."
''' complex_dic = {'name': 'json', 'hobby': ['read', ['run', {'music': 'if you'}]]} <p>{{ complex_dic.hobby.1.1.music }}</p> # if you, 获取music的值 # 数据获取方式复制时使用别名, 但是别名只能在with内部使用 {% with complex_dic.hobby.1.1.music as music %} <p>{{ music }}</p> {% endwith %} '''
自定义过滤器和标签
自定义的过滤器可以在逻辑语句中使用, 而自定义的标签不可以, 普通标签的符号也是{%%}
自定义前必须要有三步准备:
- 在应用名下新建一个名字必须叫templatetages的文件夹
- 在该文件夹内, 新建一个任意名称的py文件
- 在该py文件下书写两句代码:
from django.template import Library
,register = Library()
自定义过滤器:
# 自定义过滤器 @register.filter(name='division') def handle(x, y): # 跟默认过滤器一样, 最多接收两个参数 return x / y
使用自定义过滤器
先要将过滤器所在的文件加载过来, 类似于import
{% load my_filters_tags %} {{ 1|division:10 }}
自定义标签
可以接收任意多个参数
# 自定义标签 @register.simple_tag(name='my_tag') def handle(a, b, c): return '%s?%s?%s' % (a, b, c)
使用自定义标签
接收的多个参数必须以空格隔开
''' {% load my_filters_tags %} {% my_tag 'c' 'q' 'l' %} # c?q?l '''
自定义inclusion_tag
- 是一个函数, 能够接收外界传入的参数, 然后传递给一个html片段
- 该html片段获取数据并渲染
- 最后将渲染好的html片段返回到调用inclusion_tag的地方
# 自定义inclusion_tag @register.inclusion_tag('part_html.html') def inclusion_tag(n): lt = [] for i in range(n): lt.append('第%s项' % (i,)) return locals() # 将lt传递给html片段
使用inclusion_tag, 重要
应用场景: 当需要使用一些页面组件, 并且该页面组件需要参数动态渲染
''' part_html.html <ul> {% for ele in lt %} <li>{{ ele }}</li> {% endfor %} </ul> test.html {% load my_filters_tags %} {% inclusion_tag 3 %} '''
模板的继承
需要事先在想要使用的HTML页面上划定区域, 在继承之后, 就可以对划定的区域进行使用, 否则无法修改任何内容
一般至少有三块区域, html代码区域, css区域, js区域
- 在父页面上通过block模板语法划定区域
- 在子页面上通过extends模板语法继承父页面
- 在子页面使用block模板语法选择想要修改的区域
{% extends 'test.html' %} {% block css %} <style> h1 { color: red; } </style> {% endblock %} {% block content %} <h1>登录页面</h1> {{ block.super }} {# 在子页面上沿用父页面的内容#} {% endblock %} {% block js %} <script> alert(123) </script> {% endblock %}
模板的导入
将一个html片段当做模块的方式直接导入使用
与inclusion_tag区别:
- 模板的导入是写死的, 不支持传参,
- 而inclusion_tag支持传参, 可以根据参数不同动态渲染HTML片段
''' test111.html: <h1>这是一个特别好看的form表单</h1> {% block content %} {% include 'test111.html' %} {% endblock %} '''