第六章 day60~day69

这里写目录标题

  • 第 2 种渲染方式,label 对应的字段
  • 后端添加 label 属性
  • 第 3 种渲染方式,for+label+span(推荐使用)
  • Day 68 - 08 forms组件展示错误信息
  • Day 68 - 09 forms组件钩子函数
  • Day 68 - 10 重要参数介绍
  • Day 68 - 11 其他字段类型
  • Day 69
  • Day 60

    Day 60 - 03 静态文件配置

    Day 60 - 04 request对象方法

    Day 60 - 05 pycharm链接MySQL

    Day 60 - 06 django链接MySQL

    Day 60 - 07 django orm前戏

    Day 60 - 08 字段的增删改查

    Day 60 - 09 数据的查询

    Day 60 - 10 数据的增加

    Day 61

    Day 61 - 01 昨日内容回顾

    Day 61 - 02 今日内容概要

    Day 61 - 03 数据展示

    Day 61 - 04 数据编辑

    Day 61 - 05 数据删除

    Day 61 - 06 orm创建表关系

    Day 61 - 07 django请求生命周期流程图

    Day 61 - 08 路由匹配

    Day 61 - 09 无名有名分组

    Day 61 - 10 反向解析

    Day 62

    Day 62 - 01 昨日内容回顾

    02 今日内容概要

    03 无名有名反向解析

    04 路由分发

    05 名称空间

    06 伪静态概念

    07 虚拟环境

    08 django版本区别

    09 三板斧介绍

    10 JsonResponse对象

    11 文件上传

    12 request对象方法补充

    13 FBV与CBV

    Day 63

    Day 63 - 01 昨日内容回顾

    02 今日内容概要

    03 CBV源码剖析

    04 模版语法传值

    • 模板语法传值(locals方法,返回所有的名字)
    def index(request):
        # 模板语法可以传递的后端python数据类型
        n = 123
        f = 11.11
        ...
    
    
        def func():
            print('我被执行了')
            return '你的另一半在等你'
        
        
        class MyClass(object):
            def get_self(self):
                return 'self'
        
            @staticmethod
            def get_func():
                return 'func'
        
            @classmethod
            def get_class(cls):
                return 'cls'
            def __str__(self):
                return '你到底会不会?'
    
    
        obj=MyClass()
    
        return render(request, 'index.html', locals())
    
    
    • 总结

    • 《花括号》与《百分号》含义

    1. {{ }} 变量相关
    2. {% %} 逻辑相关
    • 传递变量
    1. {{ n }} 或 {{ f }} 或 {{ s }}
    2. 等等各种类型全部都可以传
    • 传递函数
    1. {{ func }}
    2. 函数自动帮你加括号调用,传的是返回值。
    3. 切记,不支持给函数传参数,会给你报错。
    • 传递类
    1. 类 {{ MyClass }}
    2. 对象 {{ obj }}
    3. 传类名的时候,也会自动加括号调用(实例化)
    • {{ obj.get_self }}
    • {{ obj.get_func }}
    • {{ obj.get_class }}
    • 总结:
    1. 内部能够自动判断出当前的变量名是否可以加括号调用,
    2. 如果可以就自动执行(针对函数名和类名)
    • 列表和字典取值
    1. 只能用句点符 . ,哪怕字典列表嵌套
    2. d.info.3.hobby

    05 模版语法之过滤器

    • 什么是模版语法过滤器?

    类似于一些简单的内置方法,后端传来一个变量,前端展示关于变量的信息
    Django内置 60+ 过滤器,我们了解10个左右,差不多了。后面碰到了再去记忆

    • 通用格式
      {{ 变量名|过滤器:'参数' }}

    • 下面都可以包含在 <p></p> 标签中

    1. {{ s|length }} 统计长度
    2. {{ b|default: '啥也不是' }} 默认值
    3. 后端 file_size = 123421 ,前端{{ file_size|filesizeformat }} 文件大小(自动帮你和1024做计算。)
    4. {{ l|slice:'0:4:2' }} 切片操作
    5. info = 'aeioajflvnaowenvzbonod' {{ info|truncatechars:9 }} 切取字符(例如文章摘要,加上三个点):
    6. egl = 'My name is jason, and my ...' {{ egl|truncatedwords:9 }} msg = 'I Love You and you?' 切取单词(不包含三个点了,只认空格):
    7. {{ l|cut:' ' }} 移除字符
    8. {{ l|join:'$' }} 拼接操作:
    9. {{ n|add:10 }} 拼接操作(加法 {{ s|add:msg }}
    • 日期格式化
      后端
      import datetime
      current_time = datetime.datetime.now()
      前端
      {{ current_time|date:'Y-m-d H:i:s' }}

    • safe:取消转义
      hhh = '<h1>敏敏</h1>'
      不转义:{{ hhh }}
      转义:{{ hhh|safe }}

    06 模版语法之标签

    07 自定义过滤器、标签、inclusion_tag

    08 模版的继承

    09 模版的导入

    Day 64

    Day 64 - 01 昨日内容回顾

    如果需要后台进行表格的修改(例如书本打折)

    # 修改
    user_list = models.User.objects.filter(name__contains='egon')
    for user_obj in user_list:
        user_obj.age = user_obj.id + 1
        user_obj.save()
    

    02 今日内容概要

    03 单表及测试环境准备

    • myTest.py(与manage.py同级建立)
    from django.test import TestCase
    import os
    
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
    
    if __name__ == "__main__":
        import django
        django.setup()
        from app01 import models
        from app01 import views
    
    • filter() values() values_list()返回结果的区别
    user_query = models.User.objects.filter(name__contains='egon')  
    print(user_query)  # <QuerySet [<User: 对象:egonDSB>, <User: 对象:egonAPP>,。。。
    
    user_query = models.User.objects.filter(name__contains='egon').values()
    print(user_query)  #<QuerySet [{'id': 5, 'name': 'egonDSB', 'age': 6, 'register_time': datetime.date(2020, 8, 25)},
    
    user_query = models.User.objects.filter(name__contains='egon').values_list()
    print(user_query)  # <QuerySet [(5, 'egonDSB', 6, datetime.date(2020, 8, 25)), 
    
    

    04 必知必会13条

    • 13条
    1. all()filter()
    2. values() 和 values_list() # (重点)
    3. get(pk=5)
    4. first() 和 last() # queryset 的第一个或最后一个元素
    5. distinct()
    6. order_by() 和 reverse()
    7. count() 统计数字
    8. exclude()
    9. exists()
    • 注意

    filter 是筛选条件
    values 和 values_list 是字段

    要点objects,再点方法。
    查询的返回的结果都是 queryset

    # values,values_list
    res = models.User.objects.values('name', 'age')
    # 返回结果:queryset,列表套字典
    print(res)
    res = models.User.objects.values_list('name', 'age')
    # 返回结果:queryset,列表套元组
    print(res)
    # 查看内部sql语句
    print(res.query)
    # SELECT    `day64_user`.    `name`, `day64_user`.    `age` FROM `day64_user`
    
    # distinct
    # 默认主键别忽略了
    res = models.User.objects.values('name', 'age').distinct()
    print(res)
    
    # 排序
    res = models.User.objects.values_list().order_by('age')  # 升序
    res = models.User.objects.values_list().order_by('-age')  # 降序
    print(res)
    
    # reverse  反转(前提是跟在order_by后面)
    
    # count 统计个数
    res = models.User.objects.all().count()  # 统计个数
    print(res)
    
    # exclude 排除在外
    
    # exists 基本用不到,返回bool
    

    05 神奇的双下划线查询

    • (其实就是内置方法)查询
    1. 大于小于
    2. 某个范围内
    3. 包含不包含(区分大小写)开头结尾
    4. 时间字段中定位年月日

    双下划线例题

    # 年龄大于35的数据
    res = models.User.objects.filter(age__gt=35).values('name')
    print(res)
    
    # 年龄小于35的数据
    res = models.User.objects.filter(age__lt=35).values('name')
    print(res)
    
    # 大于等于,小于等于
    res = models.User.objects.filter(age__gte=35).values('name')
    print(res)
    
    # 年龄是18,或者32,或者40
    res = models.User.objects.filter(age__in=[18, 32, 40]).values('name')
    print(res)
    
    # 年龄是18 到 40
    res = models.User.objects.filter(age__range=[18, 40]).values('name')
    print(res)
    
    # 模糊查询:名字里含有s(默认区分大小写的)
    res = models.User.objects.filter(name__contains='s').values('name')
    print(res)
    
    # 模糊查询:忽略大小写的)
    res = models.User.objects.filter(name__icontains='s').values('name')
    print(res)
    
    # 开头是j,结尾是j
    res = models.User.objects.filter(name__startswith='j').values('name')
    print(res)
    
    # 查询出注册时间是2020年,1月
    res = models.User.objects.filter(register_time__month='1').values('name')
    print(res)
    

    06 多表操作前期准备

    • 本质上只有《增加》可以考虑为针对表格的操作
    • 其他都要找到某个对象,才可以操作。

    07 外键的增删改查

    # 一对多外键增删改查
    # 增加
    # 方法1:直接写实际字段
    models.Book.objects.create(title='论语', price=998.23, publish_id=1)
    models.Book.objects.create(title='聊斋', price=444.23, publish_id=2)
    models.Book.objects.create(title='老子', price=333.23, publish_id=1)
    
    # 方法2:虚拟字段 对象 
    # 给外键创建一个对象,加到一的外键字段中去
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='红楼梦', price=666.23, publish=publish_obj)
    
    # 删除
    models.Publish.objects.filter(pk=1).delete()
    

    修改

    # update 方法
    # 编号为1的书,把出版社修改为2
    # 方法1,
    models.Book.objects.filter(pk=1).update(publish_id=2)
    # 方法2
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)
    
    # ------------------------------
    # 区别在于:
    # 1. **实际字段** 是数据库表格中的字段名称。
    # 2. **虚拟字段** 是类定义中的外键名称
    

    1. 多对多(第三张虚拟表) 增删改查, add 方法

    # 如何给书籍添加作者?
    # 这里很尴尬,因为表格不是我们创的,句点符点不到。
    book_obj = models.Book.objects.filter(pk=1).first()
    # 类似于已经到了第三张关系表了
    print(book_obj.authors)
    # 书籍id=1的书,绑定一个id为1的作者,两个三个都可以,因为一本书可以有多个作者。
    book_obj.authors.add(1)
    book_obj.authors.add(2, 3)
    
    
    author_obj = models.Author.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=2).first()
    author_obj2 = models.Author.objects.filter(pk=3).first()
    book_obj.authors.add(author_obj1, author_obj2)
    
    '''
    add 方法给第三张关系表添加数据
    括号内可以传数字,也可以传对象,并且都支持多个。
    '''
    
    # 删除
    # book为1的书,作者2没了。
    book_obj.authors.remove(2)
    
    # 同样支持多个和对象
    
    # 多对多关系的修改 -- set方法
    # 找到一个 book 对象,点虚拟字段,点set方法
    # 括号内必须是一个可迭代对象,元祖列表都可以。
    book_obj.authors.set((1, 2))
    '''
    可以是数字,也可以是对象。
    '''
    
    # 清空
    # 在第三张关系表格中,清空书籍与作者的绑定关系。
    # 括号内不要加任何参数
    book_obj.authors.clear()
    

    08 正反向的概念

    09 基于对象的跨表查询

    10 基于双下划线的跨表查询

    Day 65

    Day 65 - 01 内容回顾

    02 今日内容概要

    03 聚合查询

    • 聚合查询(聚合函数aggregate的使用)

    什么是聚合查询?

    max、min、sun、count、avg
    需要先导入5个模块 from django.db.models import Max, Min, Sum, Count, Avg
    和数据库有关,要不在django.db.models,要不在django.db

    from day64 import models
    from django.db.models import Max, Min, Sum, Count, Avg
    
    # 1. 统计书的平均价格
    res = models.Book.objects.aggregate(Avg('price'))
    print(res)
    
    # 2. 上述方法一次性使用
    res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('price'), Avg('price'))
    print(res)
    

    04 分组查询(group by)annotate

    • MySQL分组查询特点

    分组之后,只能获取到分组的依据,组内其他字段都无法直接获取了。
    严格模式: ONLY_FULL_GROUP_BY

    • 分组查询技巧

    “每个”,按什么分组
    models 后面点什么,按照什么分组。
    author_num 是我们自己定义的字段,用来存储统计每本书对应的作者个数。

    # 1. 统计每一本书的作者个数
    res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
    print(res)
    
    # 2. 统计每个出版社卖的最便宜的书的价格
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    print(res)
    
    # 3. 统计不止一个作者的图书
    # 步骤:统计每本书几个作者,过滤出作者大于1的。
    res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num')
    print(res)
    
    '''
    只要结果还是一个queryset对象,就可以无限次点queryset方法。
    比如点filter(),点到一滴都不剩。
    '''
    
    # 4. 查询每个作者出的书的总价格。
    res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
    print(res)
    
    # 5. 想按照指定的字段分组,如何处理?
    # models.Book.objects.values('price').annotate()
    
    # 只要出现分组报错,需要修改数据库严格模式。 
    

    05 F查询

    • F 查询(针对某个字段的值操作)
    # 1. 查询卖出数量大于库存数量的书籍(条件不是具体的数字)
    from django.db.models import F
    res = models.Book.objects.filter(maichu__gt=F('kucun'))
    print(res)
    
    # 2. 所有书的加个,提升500块.
    # 看一眼数据库,都加了500
    # res = models.Book.objects.update(price=F('price') + 500)
    # print(res)
    
    # 3. 所有书的名称后面加上爆款两个字
    # F 不能直接做字符串的拼接,需要导入模块。
    from django.db.models.functions import Concat
    from django.db.models import Value
    res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    print(res)
    

    06 Q查询

    • Q 查询(条件)
    # 1. 查询卖出数量大于 100 或者价格小于 600 的书籍
    # Q 包裹,可以用连接符了。
    from django.db.models import Q
    # 与或非
    res = models.Book.objects.filter(Q(maichu__gt=100) , Q(maichu__lt=600))
    res = models.Book.objects.filter(Q(maichu__gt=100) | Q(maichu__lt=600))
    res = models.Book.objects.filter(~Q(maichu__gt=100) | Q(maichu__lt=600))
    print(res)
    
    # Q的高阶用法,实现搜索功能,条件左边也是字符串,不需要是变量名
    # Q 对象 默认还是 AND 关系。
    q = Q()
    # 修改拼接关系
    q.connector = 'or'
    q.children.append(('maichu__gt', 100))
    q.children.append(('maichu__gt', 100))
    # filter 括号内支持 Q 对象
    res = models.Book.objects.filter(q)
    print(res)
    

    07 django中开启事务

    • 复习
    1. 事务的四个特性 ACID
    2. 回滚 rollback
    3. 确认 commit
    • Django 中如何简单地开启事务
    from django.db import transaction
    try:
        with transaction.atomic():
            # sql1
            # sql3
            # sql3
            # 在 with 代码块中所有的 orm 操作都是同一个事务
            pass
    except Exception as e:
        print(e)
    
    print('执行其他操作')
    

    08 orm 常用字段及参数

    常用字段说明参数
    AutoField主键字段primary_key=True
    CharFieldvarchar,verbose_name 字段注释,max_length
    IntegerFieldint
    BigIntegerFieldbig int
    DecimalFieldmax_digits=8,decimal_places=2
    EmailFieldvarcahr(254)
    DateFileddate
    DateTimeFielddatetime,auto_now,auto_now_add:
    BooleanFiled(Field)传值False/True,数据库里存0或1
    TextField(Field)存大段内容(文章,博客……)没有字数限制,bbs作业的文章字段会用。
    FileField文件字段,upload_to="/data" 给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中。/data/a.txt,后面bbs作业也会讲。upload_to="/data"
    • 更多字段,直接参考博客

    09 数据库查询优化

    • 查询优化内容
    1. orm 惰性查询
    2. only 与 defer (只查某字段与就不查某字段)
    3. select_related 与 prefetch_related (外键查询,双下划线联表操作)
    • orm 惰性查询 特点

    如果你仅仅书写了orm语句,在后面根本没有用到该语句所查询出来的参数,
    那么 orm 会自动识别,直接不执行

    例如

    res = models.Book.objects.all()
    print(res)  # 没这句不执行上面的。
    # 默认 all 会查询全部。
    
    • only 与 defer
    1. 都是返回列表套对象
    2. only 只查括号内字段,点别的字段,就要重新走数据库
    3. defer 别的都查,就不查括号内字段

    想要获取书籍表中所有书的名字

    res = models.Book.objects.values('title')
    for d in res:
        print(d.get('title'))
    

    用 only 获取书籍表中所有书的名字以及价格。

    # 如果要查看别的属性,那就还要走数据库查询语句。
    res = models.Book.objects.only('title')
    # 取出《only 列表套对象》
    for i in res:
        # 点击only括号内的字段,不会走数据库
        print(i.title)
        # 对于 only 括号内没有的字段,会重新走数据库查询。
        print(i.price)
    

    用 defer 获取书籍表中所有书的名字以及价格

    # 除了括号里的没有,其他都有
    res = models.Book.objects.defer('title')
    for i in res:
        print(i.price)
    
    • select_related prefetch_related
    1. select_related 和 prefetch_related 都和跨表操作有关
    2. 内部直接联表操作,一次性将大表所有数据封装给查询出来的对象。
    3. 无论点击book表的数据,还是publish的数据,无需再走数据库查询。
    1. select_related 括号里只能放外键字段的一对一,一对多
    2. prefetch_related 内部就是内部子查询
    res = models.Book.objects.all()
    for i in res:
        # 《书名》和《出版社名》在两张表上,
        # 每查询一次,都要走一次数据库。
        print(i.publish.name)
    

    用 select_related

    res = models.Book.objects.select_related('publish')
    for i in res:
        print(i.publish.name)
    
    # prefetch_related 内部就是内部子查询。
    # 使用者感觉不粗差距
    res = models.Book.objects.prefetch_related('publish')
    for i in res:
        print(i.publish.name)
    
    • 补充一个方法
    res = models.Book.objects.select_related('外键字段1__外键字段2__外键字段3')
    for i in res:
        print(i.publish.name)
    

    10 图书管理系统首页展示

    1. 模板制作
    2. 首页
    3. 书的增删改查

    Day 66

    Day 66 - 01 内容回顾

    02 今日内容概要

    03 图书列表展示页

    04 书籍的添加

    05 书籍的编辑

    06 书籍的删除

    07 choices参数

    • 设计一个表,比如用户表。

    性别,学历,工作经验,客户来源……
    能够列举完全的字段,那么一般情况下采用 choices 参数
    取的时候用 get_字段_display() 方法

    models.py

    class User(models.Model):
        username = models.CharField(max_length=32)
        age = models.IntegerField()
        # 性别
        gender_choice = (
            (1, '男'),
            (2, '女'),
            (3, '其他'),
        )
        gender = models.IntegerField(choices=gender_choice)
    
        '''
        gender 字段存的还是数字,
        保证 choices 字段和字段内类型一致即可。
        '''
    
    # 存,没有被列举出来的数字也可以存
    models.User.objects.create(username='jason', age=18, gender=1)
    models.User.objects.create(username='egon', age=84, gender=2)
    models.User.objects.create(username='tank', age=40, gender=3)
    models.User.objects.create(username='tony', age=30, gender=4)
    
    # 取,用 get_字段_display() 方法
    user_obj = models.User.objects.filter(pk=1).first()
    print(user_obj.gender, user_obj.get_gender_display())
    # 如果没有对应字段,那存的是什么,取出还是什么
    user_obj = models.User.objects.filter(pk=4).first()
    print(user_obj.gender, user_obj.get_gender_display())
    

    08 MTV与MVC模型

    • 这些简称没有任何卵用
    1. MTV:django 给自己起的别名,本质还是 MVC
    2. models,templates,views。
    3. models,views,controller(urls.py,)

    09 多对多三种创建方式

    1. 全自动
    2. 纯手动
    3. 半自动

    例如,图书与作者关系

    • 第一种 全自动 – 利用orm帮我们创建
    class Book(models.Model):
        ...
        authors = models.ManyToManyField(to='Author')
        ...
    

    优点

    • 不用写代码,方便,快速

    缺点

    • 第三张表的扩展性极差
    • 第二种 纯手动
    class Book2Author(models.Model):
        ...
        book_id = models.ForeignKey(to='Book')
        author_id = models.ForeignKey(to='Author')
        ...
    

    优点

    • 第三张表完全取决于你

    缺点

    • 代码太多,无法使用 orm 提供的正反向查询和简单方法(add,set,remove,clear···)
    • 不推荐使用《纯手动》
    • 第三种:半自动
    class Book(models.Model):
        ...
        authors = models.ManyToManyField(
            to='Author', 
            through='Book2Author', 
            through_field=('book', 'author'), 
        )
        ...
    
    class Author(models.Model):
        ...
    
    # Book2Author 还是需要自己写,在 Book 中关联
    class Book2Author(models.Model):
        ...
        book_id = models.ForeignKey(to='Book')
        author_id = models.ForeignKey(to='Author')
        ...
    

    半自动可以使用正反向查询,但是依旧不能使用 add,set,remove,clear···

    • 总结
    1. 需要掌握《全自动》和《半自动》,一般采用《半自动》

    10 Ajax 简介(重点,全栈必备)

    • 八个大字,精髓

    局部筛选,异步提交

    • ajax 有什么用?

    例子:github 注册

    • 动态获取用户名,实时发到后端检测

    我们学过的发送请求的方式

    1. 地址栏输入url,只有get
    2. a标签的href属性,只有get
    3. from表单,get | post
    4. ajax,get | post

    ->我们现在大部分都是 ajax。

    • 注意
    1. 不是新的编程语言,是一种使用现有标准的新方法(例如装饰器)
    2. 不需要重新加载整个网页,就与服务器交互,更新网页数据。
    3. 我们只学 jQuery 封装之后的版本。原生的不太用。(不知 jQuery,其他框架也可以,就是关键字不同,换汤不换药)
    4. 所以一定要确保导入了 jQuery。

    11 Ajax基本语法

    • 先来个总结,方便复制黏贴
    1. 后端 views.py 导入模块 from django.http import JsonResponse
    2. 视图函数返回 return JsonResponse(back_dic)
    3. 前端页面导入 jQuery <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    • 前端基本语法
    <input type="text" name="" id="d1">+
    <input type="text" name="" id="d2">=
    <input type="text" name="" id="d3">
    <p>
        <button id="btn">点我</button>
    </p>
    
    <script>
        // 用 jquery 给按钮绑定一个点击事件
        $('#btn').click(function(){
            // 朝后端发送 ajax 请求
            $.ajax({
                url: '',
                type: 'post',
                dataType: 'Json',
                data: {'i1': $('#d1').val(), 'i2': $('#d2').val()},
                success:function(args){
                    // alert(args)
                    $('#d3').val(args)
                }
            })
        })
    </script>
    
    1. 最后一个success是回调函数(形参写什么都可以)。无论输入啥,最后都是返回给回调函数。不是浏览器。
    2. 如果要返回字典的话,需要用 json 格式。
    3. 不管什么格式,前端(这个页面)收到的参数都是字符串格式。
    • 后端返回数据(不是 JsonResponse,需要解析)
    def ab_ajax(request):
        if request.method == "POST":
            print(request.POST)
            i1 = request.POST.get('i1')
            i2 = request.POST.get('i2')
            i3 = int(i1) + int(i2)
            return HttpResponse(i3)
        return render(request, 'ab_ajax.html')
    
    1. 如果后端返回的是 HttpResoponse,只能在前端,Json.parse() 自己解决
    2. 如果后端返回的是 JsonResponse ,前端会自动帮你反序列化 dataType: 'Json',
    • 后端
    from django.http import JsonResponse
    
    def ab_ajax(request):
        ...
        # 这里 d 是个字典。
        return JsonResponse(d)
        ...
    

    以后写 ajax 的时候,可以直接加上,以防万一。


    Day 67

    Day 67 - 01 内容回顾

    Day 67 - 02 今日内容概要

    Day 67 - 03 前后端传输数据编码格式

    (主要研究 POST 请求的编码格式)

    向后端发psot请求的两种方式

    1. form 表单
    2. ajax

    三种格式

    1. urlencoded
    2. formdata
    3. json
    • 第一种,form 表单
    1. 发送文本:默认 encoded
    2. 查看方法:检查 – Network – 左侧 name – 在请求头(Request Heads)里面找 content-type == encoded

    1. 最底下的 view source,可以看到 name=jason&password=123 django 后端会自动帮忙就解析到 request.POST 中。
    • 发送文件,记得 enctype="multipart/form-data"

    如果是 encoded
    只要长得是 name=jason&password=123,都会帮你解析到 request.POST 。
    但是如果把编码格式,改成 formdata
    字符还是会帮你解析到 request.POST .
    文件会解析到 request.FILES

    • 第二种,ajax

    默认也是 urlencoded
    所以,无论哪种,都可以通过 request.POST 中获得。

    Day 67 - 04 ajax发送json格式数据

    • 为什么要学这个?

    防止前后端不全用Django框架写的。
    前后端一定要保证编码格式和数据真正格式是一致的。

    1. 前端 data: JSON.stringify({'username': 'jason', 'age': ''18}) 转换成JSON格式
    2. contentType: 'application/json', // 指定编码格式,
    3. 数据是真正的 json 格式
    4. 后端 要判断 ajax 请求:if(request.is_ajax()):
    5. 在 request.POST 里面找不到,要在 request.body 中自己去找。
    6. 如果前端生成内置对象,并且有文件,一定要拒绝编码处理,文件在后端的 request.FILES 中寻找,普通键值对在 request.POST 中寻找。
    • 前端 html 文件(写死版本)
    <button class="btn btn-success" id="d1">点我</button>
    
    <script>
        // 用 jquery 给按钮绑定一个点击事件
        $('#d1').click(function(){
            $.ajax({
                url: '',
                type: 'post',
                // 转换成 json 格式
                data: JSON.stringify({'username': 'jason', 'age': '18'}),
                // 指定编码格式
                contentType: 'application/json',  
                success:function(){
    
                }
            })
        })
    </script>
    
    <!-- 朝后端发送的数据  -->
    <!-- {"username":"jason","age":"18"} -->
    
    • 方法补充:判断 ajax 请求
    if(request.is_ajax()):
        pass
    

    django对于ajax 的 json 数据,不会做任何处理。
    在 request.body 里面
    针对 json 格式数据,需要手动处理。

    # 第一种写法(全面)
    if(request.is_ajax()):
        # bytes -- str -- dict
        json_bytes = request.body
        json_str = json_bytes.decode('utf-8')
        json_dict = json.loads(json_str)
        print(json_dict, type(json_dict))
    
    
    # 第二种写法(简便)
    if(request.is_ajax()):
        # bytes -- dict (json.loads() 会先自动解码,再自动反序列化)
        json_bytes = request.body
        json_dict = json.loads(json_bytes)
        print(json_dict, type(json_dict))
    

    Day 67 - 05 ajax发送文件数据

    向后端发送普通键值对,也发送文件
    p*3>inp

    前端(通过内置对象发送,建议背诵)

    <p>username:<input type="text" name="" id="d1"></p>
    <p>password<input type="text" name="" id="d2"></p>
    <p><input type="file" name="" id="d3"></p>
    <button class="btn btn-info" id="d4">点我,有你好看</button>
    
    <script>
        // 点击按钮,向后端发送普通键值对和文件
        $('#d4').on('click', function() {
            // 1. 先生成一个内置对象
            let formDataObj = new FormData();
            // 2. 可以添加普通键值对,也可以添加文件
            formDataObj.append('username', $('#d1').val());
            formDataObj.append('password', $('#d2').val());
            formDataObj.append('myfile', $('#d3')[0].files[0]);
            // 3. 将对象基于 ajax 发送给后端
            $.ajax({
                url: '',
                type: 'post',
                // 直接放对象即可
                data: formDataObj,
                // 必须指定两个参数,告诉后端编码和浏览器,别对数据动手动脚
                contentType: false,
                processData: false,
    
                success: function(args){
                }
            })
        })
    
    </script>
    

    后端

    def ab_file(request):
        if request.is_ajax():
            if request.method == 'POST':
                print(request.POST)
                print(request.FILES)
                # 结果:
                # <QueryDict: {'username': ['jason'], 'password': ['123']}>
                # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: WechatIMG746.jpeg (image/jpeg)>]}>
                pass
        return render(request, 'ab_file.html', locals())
    
    • 总结
    1. 需要使用对象 formData
    2. 指定两个关键的参数,拒绝编码和数据处理
    3. 后端可以直接直接识别 formData 对象并且自动解析,将 普通键值对 封装到 request.POST 中,将文件封装到 request.FILES 中。

    Day 67 - 06 django自带的序列化组件

    Day 67 - 08 批量插入数据

    Day 67 - 09 自定义分页器推导过程

    Day 68

    Day 68 - 01 内容回顾

    Day 68 - 02 今日内容概要

    Day 68 - 03 自定义分页器使用

    Day 68 - 04 form组件前戏

    Day 68 - 05 forms组件类书写

    Day 68 - 06 forms校验数据

    Day 68 - 07 forms组件渲染标签

    Day 68 - 08 forms组件展示错误信息

    Day 68 - 09 forms组件钩子函数

    Day 68 - 10 重要参数介绍

    Day 68 - 11 其他字段类型

    Day 69

    Day 69 - 01 内容回顾

    Day 69 - 02 今日内容概要

    Day 69 - 03 forms组件源码

    Day 69 - 04 cookie与session简介

    Day 69 - 05 django操作cookie

    Day 69 - 06 session操作

    Day 69 - 07 CBV添加装饰器的三种方式

    Day66

    Day 66 - 01 内容回顾

    02 今日内容概要

    03 图书列表展示页

    04 书籍的添加

    05 书籍的编辑

    06 书籍的删除

    07 choices参数

    1. 啥是choices参数?

    1. 设计一个表,比如用户表。有一些字段:性别,学历,工作经验,客户来源……
    2. 这些字段的内容大量重复,并能够列举完全,那么一般情况下采用 choices 参数

    2. 如何定义choices参数?

    1. 以性别为例,元组套元组。例如gender_choice
    2. 参数models.IntegerField(choices=gender_choice)e
    3. gender 字段存的还是数字,保证 choices 子元组第一个参数和 gender 字段内类型一致即可

    models.py

    class User(models.Model):
        username = models.CharField(max_length=32)
        age = models.IntegerField()
        # 性别
        gender_choice = (
            (1, '男'),
            (2, '女'),
            (3, '其他'),
        )
        gender = models.IntegerField(choices=gender_choice)
    

    取的时候用 get_字段_display() 方法
    例如user_obj.get_gender_display()

    # 存,没有被列举出来的数字也可以存
    models.User.objects.create(username='jason', age=18, gender=1)
    models.User.objects.create(username='egon', age=84, gender=2)
    models.User.objects.create(username='tank', age=40, gender=3)
    models.User.objects.create(username='tony', age=30, gender=4)
    
    
    # 取,用 get_字段_display() 方法。--》男
    user_obj = models.User.objects.filter(pk=1).first()
    print(user_obj.gender, user_obj.get_gender_display())
    # 如果没有对应字段,那存的是什么,取出还是什么。--》4
    user_obj = models.User.objects.filter(pk=4).first()
    print(user_obj.gender, user_obj.get_gender_display())
    

    08 MTV 与 MVC 模型

    • 这些简称没有任何卵用
      • MTV:django 给自己起的别名,本质还是 MVC。
    • 全称
      • models,templates,views。
      • models,views,controller(urls.py,)

    09 多对多三种创建方式

    例如图书表与作者表关系,是多对多。
    一本书可以有多个作者,一位作者可以有多本书。

    1. 半自动(推荐)
      • 《Book》中authors = models.ManyToManyField(...),
      • 三个参数 to, through, through_field。
      • 《Book2Author》还是要自己写。
    2. 全自动(方便写,但不好扩展)
      -《Book》中:authors = models.ManyToManyField(to='Author')
    3. 纯手动(无必要)
      • 手动创建《book2author》
      • 《book2author》中:models.ForeignKey(to='Book')

    1. 半自动创建多对多关系的方法(推荐方法,要掌握)

    class Book(models.Model):
        ...
        authors = models.ManyToManyField(
            to='Author', 
            through='Book2Author', 
            through_field=('book', 'author'), 
        )
        ...
    
    class Author(models.Model):
        ...
    
    # Book2Author 还是需要自己写,在 Book 中关联
    class Book2Author(models.Model):
        ...
        book_id = models.ForeignKey(to='Book')
        author_id = models.ForeignKey(to='Author')
        ...
    

    2. 全自动(利用orm帮我们创建)

    class Book(models.Model):
        ...
        authors = models.ManyToManyField(to='Author')
        ...
    
    • 优点

      • 不用写代码,方便,快速
    • 缺点

      • 第三张表的扩展性极差

    3. 纯手动

    class Book2Author(models.Model):
        ...
        book_id = models.ForeignKey(to='Book')
        author_id = models.ForeignKey(to='Author')
        ...
    
    • 优点
      • 第三张表完全取决于你
    • 缺点
      • 代码太多,无法使用 orm 提供的正反向查询和简单方法(add,set,remove,clear···)
    • 不推荐使用《纯手动》

    10 Ajax简介

    1. 什么是 Ajax ?

    • 例子:
      -github 注册用户名时,
      页面会动态获取用户名的输入情况,实时发到后端检测是否已经被注册过
    • 八个大字,精髓
      • 局部筛选,异步提交

    • 回顾一下我们学过的提交请求方式
      1. 地址栏输入url,只有get
      2. a标签的href属性,只有get
      3. from表单,get | post
      4. ajax,get | post
    • 现在大部分都是 Ajax。
    • 我们学的是 Jquery版本。所以要确保导入了JQuery

    11 Ajax基本语法

    - 小例子:三个框,输入计算1 + 2 = 3

    1. 前端

    <input type="text" name="" id="d1">+
    <input type="text" name="" id="d2">=
    <input type="text" name="" id="d3">
    <p>
        <button id="btn">点我</button>
    </p>
    
    <script>
        // 用 jquery 给按钮绑定一个点击事件
        $('#btn').click(function(){
            // 朝后端发送 ajax 请求
            $.ajax({
                url: '',
                type: 'post',
                dataType: 'Json',
                data: {'i1': $('#d1').val(), 'i2': $('#d2').val()},
                success:function(args){
                    // alert(args)
                    $('#d3').val(args)
                }
            })
        })
    </script>
    
    • 最后一个success是回调函数(形参写什么都可以)。无论输入啥,最后都是返回给回调函数。不是浏览器。
    • 如果要返回字典的话,需要用 json 格式。
    • 不管后端提交的什么格式,前端(这个页面)收到的参数都是字符串格式

    2. 后端 | 返回数据

    def ab_ajax(request):
        if request.method == "POST":
            print(request.POST)
            i1 = request.POST.get('i1')
            i2 = request.POST.get('i2')
            i3 = int(i1) + int(i2)
            return HttpResponse(i3)
        return render(request, 'ab_ajax.html')
    
    1. 如果后端返回的是 HttpResoponse,只能在前端,Json.parse() 自己解决
    2. 如果后端返回的是 JsonResponse ,前端会自动帮你反序列化 dataType: 'Json',
    import json
    
    def ab_ajax(request):
        ...
        # 这里 d 是个字典。
        return JsonResponse(d)
        ...
    
    • 以后写 ajax 的时候,可以直接加上,以防万一。
    • Json 如何传输,看下一页。

    Day67

    Day 67 - 01 内容回顾

    1. 如果出bug了,如何排查?

    1. Pycharm 窗口提示 | 前端 console 界面
    2. 单词写错没?
    3. 浏览器缓存没有清空或者端口号冲突(正在跑之前的项目)
    4. 重启计算机

    Day 67 - 02 今日内容概要

    Day 67 - 03 前后端传输数据编码格式

    无论哪一种,最后都可以通过 request.POST 获取。

    1. 前后端传输数据编码格式(POST)

    1. 前端向后端发psot请求的两种方式

      1. form 表单
      2. ajax
    2. 三种格式

      1. urlencoded
      2. formdata
      3. json

    2. form 表单

    1. 发送文本
      • 默认 encoded
      • 查看方法
        • 检查 – Network – 左侧 name – 在请求头(Request Heads)里面找
        • content-type == encoded
        • 最底下的 view source,可以看到 name=jason&password=123
      • django 后端会自动帮忙就解析到 request.POST 中。
    2. 发送文件,<form>记得 enctype="multipart/form-data"
      • 如果是 encoded,
        • 只要长得是 name=jason&password=123,都会帮你解析到 request.POST 。
      • 但是如果把编码格式改成 formdata,
        • 字符还是会帮你解析到 request.POST
        • 文件会解析到request.FILES 中

    3. ajax

    1. 默认也是 urlencoded
    2. 所以,无论哪种,只要是字符格式,都可以通过 request.POST 中获取。

    Day 67 - 04 ajax 发送 json 格式数据

    前后端一定要保证编码格式和数据真正格式是一致的。
    比如说好的列表,最后发了个字符串。

    1. contentType: 'application/json', // 指定编码格式,
    2. 数据是真正的 json 格式
    3. 在 request.POST 里面找不到,要在 request.body 中自己去找。

    1. 前端 html 文件 | 提交 json 格式数据

    1. 把数据转成json格式
    2. 指定编码格式
    <button class="btn btn-success" id="d1">点我</button>
    <script>
        // 用 jquery 给按钮绑定一个点击事件
        $('#d1').click(function(){
            $.ajax({
                url: '',
                type: 'post',
                // 转换成 json 格式
                data: JSON.stringify({'username': 'jason', 'age': '18'}),
                // 指定编码格式
                contentType: 'application/json',  
                // 回调函数
                success:function(){}
            })
        })
    </script>
    <!-- 朝后端发送的数据  -->
    <!-- {"username":"jason","age":"18"} -->
    

    2. 方法补充:判断 ajax 请求

    if(request.is_ajax()):
        pass
    

    3. 后端 | 处理收到的 ajax json 格式数据

    1. 判断是 ajax 请求。if(request.is_ajax()): ...
    2. 数据都在 request.body 里面。
    3. json.loads(json_bytes),转换成字典,即可
    # 第一种写法(全面)
    if(request.is_ajax()):
        # bytes -- str -- dict
        json_bytes = request.body
        json_str = json_bytes.decode('utf-8')
        json_dict = json.loads(json_str)
        print(json_dict, type(json_dict))
    
    # 第二种写法(简便)
    if(request.is_ajax()):
        # bytes -- dict (json.loads() 会先自动解码,再自动反序列化)
        json_bytes = request.body
        json_dict = json.loads(json_bytes)
        print(json_dict, type(json_dict))
    

    Day 67 - 05 ajax发送文件数据(非Json)

    步骤

    1. 生成内置对象,存储数据
    2. 用 append(键值对) 的方式添加数据
    3. 注意,添加文件的时候 formDataObj.append('myfile', $('#d3')[0].files[0]);
    4. 必须添加两行代码,告诉浏览器别对数据动手动脚。参数:contentType,processData,都设置成false。

    前端

    <p>username:<input type="text" name="" id="d1"></p>
    <p>password<input type="text" name="" id="d2"></p>
    <p><input type="file" name="" id="d3"></p>
    <button class="btn btn-info" id="d4">点我,有你好看</button>
    
    <script>
        // 点击按钮,向后端发送普通键值对和文件
        $('#d4').on('click', function() {
            // 1. 先生成一个内置对象
            let formDataObj = new FormData();
            // 2. 可以添加普通键值对,也可以添加文件
            formDataObj.append('username', $('#d1').val());
            formDataObj.append('password', $('#d2').val());
            formDataObj.append('myfile', $('#d3')[0].files[0]);
            // 3. 将对象基于 ajax 发送给后端
            $.ajax({
                url: '',
                type: 'post',
                // 直接放对象即可
                data: formDataObj,
                // 必须指定两个参数,告诉后端编码和浏览器,别对数据动手动脚
                contentType: false,
                processData: false,
    			// 回调函数
                success: function(args){}
            })
        })
    </script>
    

    后端

    1. 键值对都在 reuqest.POST 中
    2. 文件在 reuqest.FILES 中
    def ab_file(request):
        if request.is_ajax():
            if request.method == 'POST':
                print(request.POST)
                print(request.FILES)
                # 结果:
                # <QueryDict: {'username': ['jason'], 'password': ['123']}>
                # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: WechatIMG746.jpeg (image/jpeg)>]}>
        return render(request, 'ab_file.html', locals())
    

    总结

    1. 需要使用对象 formData
    2. 指定两个关键的参数,拒绝编码和数据处理
    3. 后端可以直接直接识别 formData 对象并且自动解析,将 普通键值对 封装到 request.POST 中,将文件封装到 request.FILES 中。

    Day 67 - 06 django自带的序列化组件

    1. 序列化组件有什么用?

    • 因为如果前后端分离,前端不是 django 写的,那么就无法用模板语法了。
    • 所以要用 json 格式

    解决办法

    1. 再写一个接口文档(最多)
    2. 后端用户表里所有数据,发给前端。要是《列表套字典》的形式(本节课内容)

    方法1 笨办法:(手动)列表套字典

    def ab_ser(request):
        import json
        from django.http import JsonResponse
    
        user_queryset = models.User.objects.all()
        user_list = []
        for user_obj in user_queryset:
            temp = {
                'pk': user_obj.pk,
                'username': user_obj.username,
                'age': user_obj.age,
                'gender': user_obj.get_gender_display(),
            }
            user_list.append(temp)
    
        return JsonResponse(user_list, safe=False)
    

    结果(人工智能换行)

    [
        {"pk": 1, "username": "jason", "age": 18, "gender": "\u7537"}, 
        {"pk": 2, "username": "egon", "age": 84, "gender": "\u5973"}, 
        {"pk": 3, "username": "tank", "age": 40, "gender": "\u5176\u4ed6"}, 
        {"pk": 4, "username": "tony", "age": 30, "gender": 4}
    ]
    

    方法2 serializers 模块

    def ab_ser(request):
        from django.core import serializers
        user_queryset = models.User.objects.all()
        # 自动帮你变成 json 格式字符串,而且非常全面
        res = serializers.serialize('json', user_queryset)
        return HttpResponse(res)
    

    结果

    [
        {
            "model": "day66.user", 
            "pk": 1, 
            "fields": {"username": "jason", "age": 18, "gender": 1}
        }, 
        {
            "model": "day66.user", 
            "pk": 2, 
            "fields": {"username": "egon", "age": 84, "gender": 2}
        }, 
        {
            "model": "day66.user", 
            "pk": 3, 
            "fields": {"username": "tank", "age": 40, "gender": 3}
        }, 
        {
            "model": "day66.user", 
            "pk": 4, 
            "fields": {"username": "tony", "age": 30, "gender": 4}
        }
    ]
    
    • 这种情况,要写个文档告诉前端对应关系。
    • 前端只要点点点就可以了

    Day 67 - 08 批量插入数据

    1. 笨办法

    1. 一条一条往数据库里 create。
    def ab_batch_insert(request):
        '''
        1. 先给Book,插入一万条数据
        1. 所有数据查询并展示到页面。
        :param request:
        :return:
        '''
        for i in range(10000):
            models.Book.objects.create(title='第%i本书' %i)
    
        book_queryset = models.Book.objects.all()
        return render(request, 'ab_batch_insert.html', locals())
    

    2. 批量插入(bulk_create)

    1. 先建列表 book_list ,往里面添加一大堆 book_obj
    2. 直接 bulk_create,插入列表 models.Book.objects.bulk_create(book_list)
    def ab_batch_insert(request):
    
        book_list = []
        for i in range(10000):
            book_obj = models.Book(title='第%i本书' %i)
            book_list.append(book_obj)
    
        models.Book.objects.bulk_create(book_list)
        book_queryset = models.Book.objects.all()
    
        return render(request, 'ab_batch_insert.html', locals())
    

    Day 67 - 09 自定义分页器推导过程

    1. 分页逻辑

    # 1. 访问哪一页?通过get请求,如果没有,就展示第一页
    current_page = request.GET.get('page', 1)
    
    # 数据类型转换
    try:
        current_page = int(current_page)
    except Exception:
        current_page = 1
        
    # 2. 每页展示多少条?
    per_page_num = 10
    # 分页规律
    # 3. 起始位置
    start_page = (current_page - 1) * per_page_num
    # 4. 终止位置
    end_page = current_page * per_page_num
    '''
    这要计算页面显示多少页的按钮
    '''
    # 展示所有数据 (切片操作)
    book_queryset = models.Book.objects.all()[start_page:end_page]
    

    2. 第二部分

    # 1. 访问哪一页?通过get请求,如果没有,就展示第一页
    current_page = request.GET.get('page', 1)
    # 数据类型转换
    try:
        current_page = int(current_page)
    except Exception:
        current_page = 1
    
    # 1. 每页展示多少条?
    per_page_num = 10
    # 规律 start_page = (current_page - 1) * per_page_num
    # 1. 起始位置
    start_page = (current_page - 1) * per_page_num
    # 1. 终止位置
    end_page = current_page * per_page_num
    
    # 分页,计算页面要显示多少页的按钮
    book_list = models.Book.objects.all()
    all_count = book_list.count()
    page_count, more = divmod(all_count, per_page_num)
    if more:
        page_count += 1
    
    page_html = ''
    xxx = current_page
    if current_page < 6:
        current_page = 6
    for i in range(current_page-5, current_page+5):
        # 做一个高亮显示
        if xxx == i:
            page_html += '<li class="active"><a href="?page=%s">%s</a></li>' %(i, i)
        else:
            page_html += '<li><a href="?page=%s">%s</a></li>' %(i, i)
    
    # 展示所有数据(切片操作)
    book_queryset = models.Book.objects.all()[start_page:end_page]
    
    return render(request, 'ab_batch_insert.html', locals())
    

    Day 68

    Day 68 - 01 内容回顾

    Day 68 - 02 今日内容概要

    Day 68 - 03 自定义分页器使用

    1. 知识补充
      1. @property,将方法伪装成属性,不需要加括号也可以调用
      2. bootstrap 代码都不用拷贝了,直接调用下面就可以
    2. 拷贝大法步骤
      1. utils 文件夹,新建 myPage.py,拷贝下列代码。
      2. 可以在每个应用下,也可以在全局创建。

    1. 自定义分页器源代码

    myPage.py

    class Pagination(object):
        def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param pager_count:  最多显示的页码个数
            """
            try:
                current_page = int(current_page)
            except Exception as e:
                current_page = 1
    
            if current_page < 1:
                current_page = 1
    
            self.current_page = current_page
    
            self.all_count = all_count
            self.per_page_num = per_page_num
    
            # 总页码
            all_pager, tmp = divmod(all_count, per_page_num)
            if tmp:
                all_pager += 1
            self.all_pager = all_pager
    
            self.pager_count = pager_count
            self.pager_count_half = int((pager_count - 1) / 2)
    
        @property
        def start(self):
            return (self.current_page - 1) * self.per_page_num
    
        @property
        def end(self):
            return self.current_page * self.per_page_num
    
        def page_html(self):
            # 如果总页码 < 11个:
            if self.all_pager <= self.pager_count:
                pager_start = 1
                pager_end = self.all_pager + 1
            # 总页码  > 11
            else:
                # 当前页如果<=页面上最多显示11/2个页码
                if self.current_page <= self.pager_count_half:
                    pager_start = 1
                    pager_end = self.pager_count + 1
    
                # 当前页大于5
                else:
                    # 页码翻到最后
                    if (self.current_page + self.pager_count_half) > self.all_pager:
                        pager_end = self.all_pager + 1
                        pager_start = self.all_pager - self.pager_count + 1
                    else:
                        pager_start = self.current_page - self.pager_count_half
                        pager_end = self.current_page + self.pager_count_half + 1
    
            page_html_list = []
            # 添加前面的nav和ul标签
            page_html_list.append('''
                        <nav aria-label='Page navigation>'
                        <ul class='pagination'>
                    ''')
            first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
            page_html_list.append(first_page)
    
            if self.current_page <= 1:
                prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
            else:
                prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
    
            page_html_list.append(prev_page)
    
            for i in range(pager_start, pager_end):
                if i == self.current_page:
                    temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
                else:
                    temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
                page_html_list.append(temp)
    
            if self.current_page >= self.all_pager:
                next_page = '<li class="disabled"><a href="#">下一页</a></li>'
            else:
                next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
            page_html_list.append(next_page)
    
            last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
            page_html_list.append(last_page)
            # 尾部添加标签
            page_html_list.append('''
                                               </nav>
                                               </ul>
                                           ''')
            return ''.join(page_html_list)
    
    

    2. 使用自定义分页器代码

    views.py

    from utils.myPage import Pagination
    

    前端

    {% for book_obj in page_queryset %}
        <p>{{ book_obj.title }}</p>
    {% endfor %}
    
    {{ page_obj.page_html|safe }}
    

    Day 68 - 04 forms组件前戏

    1. forms 组件能干什么?

    不使用 ajax 校验数据,直接返回给前端。

    1. 渲染 html 代码
    2. 校验数据
    3. 展示提示信息

    需求

    1. 写一个 html 页面,获取用户名密码,form 表单提交数据
    2. 后端判断用户名和密码是否符合一定条件
      1. 用户名中不能含有金瓶梅
      1. 密码不能少于三位
    3. 将提示信息展现到 html 页面中。

    解决思路

    • 前端页面
    1. 用户名和密码的输入框后面,放一个看不见的span标签,用模板语法给它传值。
    2. 模板语法内容:{{ back_dic.username }}
    • 后端views
    1. 自己定义一个字典:back_dic = {'username': '', 'password': ''}
    2. key 是每个变量的名字,value 先是空的。
    3. 如果有错误,修改 key 对应的 value。
    4. 写一个分支,如果校验正确,返回 back_dic,提示正确。如果校验错误,返回 back_dic,提示输入有误。

    小例子
    前端页面

    <form action="" method="post">
        <p>username:
            <input type="text" class="form-control" name="username">
            <span style="color: red">{{ back_dic.username }}</span>
        </p>
        <p>password:
            <input type="text" class="form-control" name="password">
            <span style="color: red">{{ back_dic.password }}</span>
        </p>
    
        <input type="submit" class="btn- btn-info">
    </form>
    

    后端视图

    def ab_form(request):
        back_dic = {'username': '', 'password': ''}
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if '金瓶梅' in username:
                back_dic['username'] = '不符合社会主义核心价值观'
            if len(password) < 3:
                back_dic['password'] = '太短了'
        return render(request, 'ab_form.html', locals())
    

    注意

    1. 数据校验放在哪里?
      1. 前端可有可无,但是后端校验必须要有。
      2. 因为前端校验可以通过爬虫绕过。
    2. 购物网站原理
      1. 获取用户所选择的商品的主键值
      2. 后端查询出价格,再次计算一遍
      3. 如果和前端一致,那么完成支付。

    Day 68 - 05 forms组件类书写

    1. 主要记忆的代码

    1. from django import forms (导入模块)
    2. class MyForm(forms.Form): (定义类)
    3. username = forms.CharField(..., ...) (类似于orm语句)
    4. def clean_username(self): ... (局部钩子)
    5. def clean(self): ... (全局钩子)

    2. 后面几节课的内容都在这里了

    views.py

    from django import forms
    
    
    # forms 组件
    class MyForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名',
                                   error_messages={'min_length': '最少3位',
                                                   'max_length': '最多8位',
                                                   'required': '不能为空',
                                                   })
        password = forms.CharField(min_length=3, max_length=8, label='密码',
                                   error_messages={'min_length': '最少3位',
                                                   'max_length': '最多8位',
                                                   'required': '不能为空',
                                                   })
        confirm_password = forms.CharField(min_length=3, max_length=8, label='密码',
                                   error_messages={'min_length': '最少3位',
                                                   'max_length': '最多8位',
                                                   'required': '不能为空',
                                                   })
        email = forms.EmailField(error_messages={'invalid': '格式错误',
                                                 'required': '不能为空',
                                                 })
    
        # 局部钩子
        def clean_username(self):
            username = self.clean_data.get('username')
            if '666' in username:
                self.add_error('username', '名称不能包含666')
            return username  # 钩子放回去
    
        # 全局钩子
        def clean(self):
            password = self.clean_data.get('password')
            confirm_password = self.clean_data.get('confirm_password')
            if not password == confirm_password:
                self.add_error('confirm_password', '两次密码不一致')
            return self.cleaned_data  # 钩子放回去,返回全部数据
    

    Day 68 - 06 forms校验数据

    • 额外学习

    python django 的测试环境,测试forms组件
    找到需要测试的文件
    Pycharm 下方,点击一下:python console

    • 逐行输入
    from app01 import models
    from app01 import views
    form_obj = views.MyForm({'username':'jason', 'password':'123', 'email':'112233'})
    
    form_obj.is_vaild()  # 返回True或False,数据是否全部合法
    form_obj.cleaned_data()  # 返回一个字典,包含正确的kv对
    form_obj.errors()  # 返回一个字典,包含错误的kv对,错误字段+错误原因(list)
    
    • 补充少传、穿错的情况
    '''
    	多传一个不需要的值
    '''
    form_obj = views.MyForm({'username':'jason', 'password':'123', 'email':'112233', 'hobby':'study'})
    
    form_obj.is_vaild()  # True
    form_obj.cleaned_data()  # 返回一个字典,除了多出来的hobby
    form_obj.errors()  # 空字典:{}
    
    '''
    	少传一个kv值对
    '''
    form_obj = views.MyForm({'username':'jason', 'password':'123'})
    
    form_obj.is_vaild()  # False
    form_obj.cleaned_data()  # 返回一个字典,两个正确的
    form_obj.errors()  # {'email':['This field is required.']}
    

    • 验证 form校验组件
    1. python console 中写测试(我的没法用)
    2. 通过 myTests 验证报错信息
    3. 结论:有几个参数,只找几个参数,多了的不管,少了有错

    mytests.py

    # 默认情况下,所有的字段必须传值
    form_obj = views.MyForm({'username': 'jason', 'password': '123',})
    print(form_obj.is_valid())
    print(form_obj.cleaned_data)
    print(form_obj.errors)
    # 结果
    # {'username': 'jason', 'password': '123'}
    # < ul class ="errorlist" > < li > email < ul class ="errorlist" > < li > This field is required.< / li > < / ul > < / li > < / ul >
    

    Day 68 - 07 forms组件渲染标签

    需求

    写一个页面,在页面中获取用户名、密码、邮箱。
    有了form组件,所有需要获取用户输入的标签,都不用自己写了。

    • views.py 定义函数(一行搞定)
    def ab_form(request):
        # 1. 产生一个空对象
        form_obj = MyForm()
        # 2. 直接将空对象传给 html 页面
    
        return render(request, 'index.html', locals())
    
    • index.html 前端页面

    第 1 种渲染方式,以下三者选一。

    缺点:封装程度太高,只适合本地测试

    <form action="" method="post">
        {{ form_obj.as_p }}
        {{ form_obj.as_ul }}
        {{ form_obj.as_table }}
        <input type="submit" class="btn- btn-info">
    </form>
    

    第 2 种渲染方式,label 对应的字段

    优点:封装程度适中,可以自己决定 inp 在哪里,label 在哪里

    缺点;要写的代码太多,一般情况下也不用

    <form action="" method="post">
        <p>第 2 种渲染方式</p>
        <p>{{ form_obj.username.label }} {{ form_obj.username }}</p>
        <p>{{ form_obj.password.label }} {{ form_obj.password }}</p>
        <p>{{ form_obj.email.label }} {{ form_obj.email }}</p>
        <input type="submit" class="btn- btn-info">
    </form>
    

    后端添加 label 属性

    username = forms.CharField(min_length=3, max_length=8, label='用户名',)
    

    第 3 种渲染方式,for+label+span(推荐使用)

    1. for循环
    2. .label 和 form
    3. 加一个看不见的sapn
    <form action="" method="post">
        {% for form in form_obj %}
            <p>
                {{ form.label }} : {{ form }}
                <span style="color: red">{{ form.errors }}</span>
            </p>
        {% endfor %}
        <input type="submit" class="btn- btn-info">
    </form>
    
    class MyForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名',)
        password = forms.CharField(min_length=3, max_length=8, label='密码',)
        confirm_password = forms.CharField(min_length=3, max_length=8, label='密码',)
        email = forms.EmailField()
    

    推荐写法

    1. for循环
    2. 用点lable放置提示信息。
    3. 添加一个 span,打印错误信息(点errors,可自定义)

    Day 68 - 08 forms组件展示错误信息

    • 后端代码
      • 为什么每次刷新页面点时候,输入框内的信息才不会消失?

    一开始一定要产生一个form_obj 空对象
    并且 if 判断:if reuqest.method == 'POST':内和外的 form_obj 都要名字一致。
    这样,你输入了什么,最后还会原原本本返回给前端页面,看上去和没消失一样。

    def login(request):
    	form_obj = MyForm()
    	if reuqest.method == 'POST':
    		form_obj = MyForm(request.POST)
    		if form_obj.is_valid():
    			return HttpResponse('OK')
    		else:
    			pass
    	return render(request, 'login.html', locals())
    
    • 前端
    1. 前端校验弱不禁风,如果要打破前端校验,直接修改检查即可。,默认浏览器不做校验,标签内添加属性 novalidate :<form action="" method="post" novalidate>
    2. 后方展示错误提示信息{{ form.errors.0 }}要点0。因为默认form.errors是一个字典,会帮你自动生成<ul>标签
    <form action="" method="post">
        {% for form in form_obj %}
            <p>
                {{ form.label }} : {{ form }}
                <span style="color: red">{{ form.errors.0 }}</span>
            </p>
        {% endfor %}
        <input type="submit" class="btn- btn-info">
    </form>
    

    Day 68 - 09 forms组件钩子函数

    1. 什么是钩子函数?

    在特定节点触发

    • form 组件钩子有哪些?什么情况下用?
    1. 局部钩子
    2. 全局钩子

    局部钩子,例如:用户名,需要给某个字段增加校验规则的时候使用。
    全局钩子,例如:确认密码,多个字段增加校验规则的时候使用


    2. 怎么写form 组件的钩子函数?

    1. 在 MyForm 类里面写两个函数。

    def clean_username(self):
    def clean(self):

    1. 钩子函数中,获取 MyForm 类里面的参数

    username = self.clean_data.get('username') 获某个 key
    self.add_error('username':'xxx') 给某个 key 添加错误信息

    1. 钩子放回去

    return username # 钩子放回去 局部钩子
    return self.cleaned_data # 钩子放回去,返回全部数据 全局钩子

    1. 修改 CSS样式,就提交以下参数

    widget=forms.widget.TextInput(attrs={'class': 'form-control'})
    attrs 属性,里面以字典形式放 css 属性


    代码展示:

    class MyForm(forms.Form):
        username = forms.CharField(
            min_length=3, max_length=8, label='用户名',
            error_messages={'min_length': '最少3位',
                            'max_length': '最多8位',
                            'required': '不能为空',
                            })
    
        password = forms.CharField(
            min_length=3, max_length=8, label='密码',
            error_messages={'min_length': '最少3位',
                            'max_length': '最多8位',
                            'required': '不能为空',
                            })
    
        confirm_password = forms.CharField(
            min_length=3, max_length=8, label='密码',
            error_messages={'min_length': '最少3位',
                            'max_length': '最多8位',
                            'required': '不能为空',
                            })
    
        email = forms.EmailField(
            error_messages={'invalid': '格式错误',
                            'required': '不能为空',
                            })
    
        # 局部钩子
        def clean_username(self):
            username = self.clean_data.get('username')
            if '666' in username:
                self.add_error('username', '名称不能包含666')
            return username  # 钩子放回去
    
        # 全局钩子
        def clean(self):
            password = self.clean_data.get('password')
            confirm_password = self.clean_data.get('confirm_password')
            if not password == confirm_password:
                self.add_error('confirm_password', '两次密码不一致')
            return self.cleaned_data  # 钩子放回去,返回全部数据
    
    

    Day 68 - 10 重要参数介绍

    1. form 组件其他参数及知识点(其他看看博客)

    字段含义填充
    lable字段名
    error_message自定义报错信息
    initial默认值
    required字段是否必须填写

    2. 其他验证

    Json博客园(django-form组件)

    参考:

    • RegexValidator验证器(要导入模块,是一个列表,可以写多个正则)
    • 正则校验:别自己写,网上找
    phone = forms.CharField(
        validators=[
            RegexValidator(r'[0-9]+&', '请输入数字'),
            RegexValidator(r'159[0-9]+&', '必须以159开头'),
        ]
    )
    

    Day 68 - 11 其他字段类型

    hobby = forms.MultipleChoiceField(
    	choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
    	label="爱好",
    	initial=[1, 3],
    	widget=forms.widgets.SelectMultiple()
    )
    
    • 规律

    字段只要是单选框,都是 字段 = forms.ChoiceField(...)
    字段只要是多选框,都是 字段 = forms.MultipleChoiceField(...)
    widget = forms.widgets.什么(),就是什么选择标签

    Day 69

    Day 69 - 01 内容回顾

    • 封装程序的思路
    1. 先写面条版,完成功能再去考虑优化
    2. 尝试封装函数
    3. 尝试面向对象

    Day 69 - 02 今日内容概要

    1. forms 组件
    2. cookie 与 session
    3. django 中间件(目前最好的)
    4. csrf 跨站请求伪造
    5. 视图函数(CBV)如何添加装饰器

    Day 69 - 03 forms组件源码

    • forms 组件源码的切入点

    form_obj.is_valid()

    • forms.py 源码
    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not self.errors
    
    1. self.is_bound 和 not self.errors 必须同时为 True
    2. self.is_bound 只要有值,就是 True。
    3. errors 通过 @property,伪装成属性。
    # 关于 bound
    def __init__(self, data=None, ...):
        self.is_bound = data is not None or files is not None
    
    # 关于 errors
    @property
    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors
        
    # 精髓所在,校验功能,钩子功能,都出自于 full_clean 方法。
    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound:  # Stop further processing.
            return
        self.cleaned_data = {}
        # If the form is permitted to be empty, and none of the form data has
        # changed from the initial data, short circuit any validation.
        if self.empty_permitted and not self.has_changed():
            return
    
        self._clean_fields()  # 校验字段源码
        self._clean_form()
        self._post_clean()
    
    • 回顾一下反射的知识
    1. 有了反射,可以自己通过字符串的形式,给对象添加/修改/删除属性方法等等操作。
    2. 主要有 setattr / getattr / hasattr / delattr方法
    3. 如果要修改函数的话,使用lambda表达式。
    '''
    	定义一个类
    '''
    >>> class Foo(object):
    ...     def __init__(self):
    ...             pass
    ... 
    >>> obj = Foo()
    
    '''
    	继承关系
    '''
    >>> isinstance(obj, Foo)
    True
    >>> issubclass(Foo, object)
    True
    >>> setattr(obj, 'sb', True)
    >>> obj.sb
    True
    >>> obj.sb()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'bool' object is not callable
    
    '''
    	设置成函数,加括号调用,注意参数要把对象自己传进去。
    '''
    >>> setattr(obj, 'sb', lambda self, name: print(name + 'sb'))
    >>> obj.sb('egon')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: <lambda>() missing 1 required positional argument: 'name'
    >>> obj.sb(obj, 'egon')
    egonsb
    >>> setattr(obj, 'sb', lambda self, name: print(name + '_dsb'))
    >>> getattr(obj, 'sb')(obj, 'egon')
    egon_dsb
    >>> 
    
    • 加括号调用
    >>> setattr(obj, 'p', lambda : print('xxx'))
    >>> obj.p()
    xxx
    >>> name = 'p'
    >>> getattr(obj, name)()
    xxx
    >>> 
    

    Day 69 - 04 cookie与session简介

    • 保存在浏览器的信息都可以叫 cookie
    1. 用户一次登录成功以后,信息自动保存在浏览器本地
    2. 之后访问的时候,自动将保存在本地浏览器的用户名和密码发送给服务端
    3. 服务端获取后自动验证。

    1. cookie

    1. 保存在浏览器的 kv键值对,都可以叫 cookie
    2. 都是键值对的形式
    3. 安全优化 1
      1. 当登录成功以后,服务端产生一个随机字符串,交给浏览器客户端保存
      2. 服务端用 kv键值对的形式保存这个随机字符串
      3. 形式:随机字符串1:用户1信息
      4. 之后访问,都带着这个随机字符串
      5. 隐患:如果你拿到了随机字符串,也可以冒充当前用户访问。
    4. 安全优化 2
      1. session

    2. session

    1. 保存在服务端的 kv键值对(可以有多个)
    2. 基于 cookie 工作,其实大部分保存用户状态的操作,都是通过 cookie 的。
    3. 缺点:经不住量大

    3. token(下次再讲)

    1. 服务端不再保存数据,
    2. 登录成功之后,用自己独有的方式,将用户信息加密处理
    3. 加密以后,密文拼接在用户信息后面,整体返回给浏览器保存。
    4. 浏览器下次访问的时候,依旧是用户信息+密文两段,服务端切取用户信息段,再次加密。跟尾部密文段进行比对。

    4. jwt 认证(后期会讲)

    5. 小总结

    1. cookie 在浏览器
    2. session 在服务端
    3. session 基于 cookie 工作。
    4. 大部分保存用户状态操作,都需要 cookie

    Day 69 - 05 django操作cookie

    1. 为什么要 cookie 操作

    因为http协议是无状态的,如果一个人1000次访问,1000次都是初见。

    查看方法

    浏览器 / applications / cookies,里面有很多键值对

    1. 所有要用户登录的网站,都要保存cookie
    2. 当然浏览器可以选择拒绝保存(设置里面)
      1. 后果:然后登录页面都没了

    2. cookies 最简单的操作原理

    • 设置 cookie 和获取 cooke
    ''' 设置 '''
    obj = HttpResponse()
    obj.set_cookie(key, value)
    obj.set_signed_cookie(key, value, salt='盐')
    
    ''' 获取 '''
    request.COOKIES.get(key)
    request.get_signed_cookie(key, salt='盐')
    
    • 返回 cookie
    ''' 原本的 views 函数返回值 '''
    return HttpResponse(...)
    
    
    ''' 操作 cookies 就改成这样 '''
    obj1 = HttpResponse()
    ...
    return obj1
    

    4. 操作 cookie,写一个真正的登录功能

    1. 目标函数外面套上装饰器,把 request 拿出来
      1. 获取目标url。
      1. 如果从 cookie 获取到了用户名,那就执行目标函数(跳转过去)
      1. 如果没有,就重定向到登录页面,redirect(’/day66/login/?next=%s’ %target_url)
    2. 登录页面中,分离出 next 参数。
      1. 如果登录上了,就走到下一个。
      1. 万一下一个是空,就到home
    # 装饰器
    def login_auth(func):
    	# request 要单独拿出来
        def inner(request, *args, **kwargs):
            target_url = request.get_full_path()
            if request.COOKIES.get('username'):
                res = func(request, *args, **kwargs)
                return res
            else:
                return redirect('/day66/login/?next=%s' %target_url)
        return inner
    
    
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'jason' and password == '123':
                target_url = request.GET.get('next')  # 获取用户上一次想要访问的 ur(这个结果可能是 None)
                if target_url:
                    obj = redirect(target_url)
                else:
                    obj = redirect('/day66/home/')
                # 保存用户状态(可以设置超时时间,3秒,ie浏览器就是expires=3)
                obj.set_cookie('username', 'jason666', max_age=3)
                # 跳转到一个用户登录才能看到的页面
                return obj
        return render(request, 'login.html', locals())
    
    
    @login_auth
    def home(request):
        return HttpResponse('我是 home 登录才能看到我哦')
    
    
    @login_auth
    def index(request):
        return HttpResponse('我是 index 登录才能看到我哦')
    
    
    @login_auth
    def func(request):
        return HttpResponse('我是 func 登录才能看到我哦')
    

    5. 设置 cookie 超时时间

    • obj.set_cookie() 中设置参数,都是以秒为单位。
    1. max_age=3 3 秒超时
    2. expires (针对 ie)
    • 主动注销,删除 cookie 。
     @login_auth
     def logout(request):
         obj = redirect('/day66/login/')
         # 写上要删除的键值对
         obj.delete_cookie('username')
         return obj
    

    Day 69 - 06 session操作

    1. 什么是 session?

    session 保存在服务端,返回给客户端一个随机字符串
    sessionid : 随机字符串

    应用场景

    1. 用户登录
    2. 当需要保存一段数据的时候,也可以操作session表,例如验证码
    ''' 设置 session '''
    request.session['key'] = value
    
    ''' 获取 session  '''
    request.session.get('key')
    

    注意

    1. 有一张默认的django_session表来存储数据.
      1. session_key,
      2. session_data,
      3. expire_date, 过期时间
    2. 在新项目中,一定要要执行过数据库迁移命令,才能使用
    3. 查看方法
      1. chrome / Application / storage / cookie
      2. Safari / 存储空间 / cookie

    过期时间

    默认的过期时间是14天,但是可以自己手动设置。

    2. 如何操作和获取 session

    def set_session(request):
        # 设置 session
        request.session['hobby'] = 'girl'
        request.session['hobby1'] = 'girl1'
        # 设置超时时间
        request.session.set_expiry(0)
        return HttpResponse('嘿嘿嘿')
    

    设置 session 的时候,内部

    1. 自动帮你生成一个随机字符串
    2. 将随机字符串和对应的数据存储到 django_session 表中(不是直接生效的,分为两小步)
      1. 在内存中产生操作数据的缓存
      2. 在经过中间件 SessionMiddlewre 的时候,才真正操作数据库
    3. 随机字符串返回给浏览器保存
    def get_session(request):
        print(request.session.get('hobby'))
        return HttpResponse('哈哈哈')
    

    获取session 的时候,内部

    1. 自动从浏览器请求头中获取 sessionid 对应的随机字符串
    2. 拿着随机字符串去 django_session 表中查找对应数据
    3. 比对
      1. 比对上了,就取出对应数据,并封装到 request.session 字典中
      2. 比对不上,request.session.get()返回none

    其他

    1. session 可以设置多个键值对
    2. django_session 表中数据条数取决于浏览器
      1. 同一个计算机同一个浏览器只有一条数据生效。
      2. 同一个ip,同一个计算机,不同浏览器,就不同数据。
      3. 当过期的时候,短暂可能出现多条,但是内部会自动清理。
      4. 主要为了节省服务端资源
    3. session 也可以设置过期时间(四种类型的参数)。
    request.session.set_expiry(0)
    

    设置过期时间
    request.session.set_expiry(0) 的参数

    1. 整数 -> 秒数
    2. 日期对象 -> 到指定日期失效
    3. 0 -> 关闭浏览器就自动失效
    4. 不写 -> 取决于 django 内部全局 session 失效时间(默认14天)
    • 手动清除session
    # 浏览器和服务端都清空(推荐使用)
    request.session.flush()  
    
    # 只删服务端(不太用)
    request.session.delete()   
    
    • session 是保存在服务端的,但是位置可以有多种选择
    1. MySQL
    2. 文件
    3. redis
    4. memache
    5. ···

    Day 69 - 07 CBV添加装饰器的三种方式

    导入模块

    CBV中,django不建议直接给方法加装饰器,要导入模块
    from django.utils.decorators import method_decorator

    方式 1 ,直接在方法上面装

    # 导入 views 和 方法装饰器
    from django import views
    from django.utils.decorators import method_decorator
    
    
    class MyLogin(views):
        # 方式 1
        @method_decorator(login_auth)
        def get(self):
            return HttpResponse('get 请求')
    
        def post(self):
            return HttpResponse('post 请求')
    

    方式 2,在类上面装

    可以装多个,可以针对不同的方法加不同的装饰器

    from django import views
    from django.utils.decorators import method_decorator
    
    
    @method_decorator(login_auth, name='get')
    @method_decorator(login_auth, name='post')
    class MyLogin(views):
        def get(self):
            return HttpResponse('get 请求')
    
        def post(self):
            return HttpResponse('post 请求')
    

    方式 3,重写,加在dispatch方法上

    直接作用于所有方法

    from django import views
    from django.utils.decorators import method_decorator
    
    
    class MyLogin(views):
        @method_decorator(login_auth)
        def dispatch(self, request, *args, **kwargs):
            pass
    
        def get(self):
            return HttpResponse('get 请求')
    
        def post(self):
            return HttpResponse('post 请求')
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值