day52总结

时光怂恿深爱的人放手 提交于 2019-12-06 08:03:57

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方法
  • 源码只看自己能看懂的, 不要纠结于看不懂的部分
  1. 执行类方法as_view(), 返回view
  2. 正则表达式匹配到"login/", 执行view(),
  3. view()返回dispatch(request, ...),
  4. 执行dispatch(request, ...), 会拿到请求方式字符串, 通过反射查找对象中的方法
  5. 如果找到, 将该方法记录给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 %}
'''

自定义过滤器和标签

自定义的过滤器可以在逻辑语句中使用, 而自定义的标签不可以, 普通标签的符号也是{%%}

自定义前必须要有三步准备:

  1. 在应用名下新建一个名字必须叫templatetages的文件夹
  2. 在该文件夹内, 新建一个任意名称的py文件
  3. 在该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

  1. 是一个函数, 能够接收外界传入的参数, 然后传递给一个html片段
  2. 该html片段获取数据并渲染
  3. 最后将渲染好的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区域

  1. 在父页面上通过block模板语法划定区域
  2. 在子页面上通过extends模板语法继承父页面
  3. 在子页面使用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 %}
'''
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!