zoukankan      html  css  js  c++  java
  • python笔记-20 django进阶 (model与form、modelform对比,三种ajax方式的对比,随机验证码,kindeditor)

    一、model深入

    1、model的功能

    1.1 创建数据库表

    1.2 操作数据库表

    1.3 数据库的增删改查操作

    2、创建数据库表的单表操作

    2.1 定义表对象

    class xxx(models.MODEL)

    2.2 定义字段

     

    CharField
    EmailField
    TextField
    IntegerField
    AutoField
    BooleanField
    DateField
    DateTimeField
    GenericIPAddressField
    IntegerField(choices=)#此choices可以搭配djangoAdmin使用,要和form中的choices分开
    

     

    2.3 定义字段参数 

     

    null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    error_messages     #与form中的error_message分开 models的验证功能较弱,故基本用不上
    validators         #自定义认证规则 models的验证功能较弱,故基本用不上

     

    2.4 定制元信息 class Meta

    2.4.1 定制表名 

    db_table='tb1' #数据库中表名就叫tb1,不再是默认的app+下划线+类名

    2.4.2 联合(唯一)索引

    普通索引 db_index (普通索引非元信息,摆在此处做对比)

    唯一索引 unique   (唯一索引非元信息,摆在此处做对比)

    联合索引 db_together

    联合唯一索引 unique_together

    #普通索引 
    db_index=true #加快查找速度(数据库中会针对每个索引单独创建一个文件),一列数据一个文件
    
    #联合索引
    index_together=[('name','sex'),] #将多列数据生成一个索引文件
    
    #联合唯一索引
    unique_together=[('name','sid'),] #与联合索引相同,同时加上唯一性限制,组合只能唯一

    2.4.3 最左前缀

    ->select * from where name=xxx 命中
    ->select * from where name=xxx and sex=xxx 命中
    ->select * from where sex 不能命中

    2.4.4 djangoadmin及modelform中的显示名(元信息)

    verbose_name='admin名称'
    verbose_name_pluar='admin名称复数'

    3、创建数据库表的多表操作

    3.1 ForeignKey 一对多

    3.1.1 foreginkey的实质
    ->表示关系
    ->约束

    models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users',on_delete=models.CASC)

    3.2 OnetoOne 一对一

    3.2.1 OneToONe的本质

    ->foreginkey的基础

    ->唯一约束

    3.3 ManyToMany多对多

    3.3.1 ManyToMany的本质
    ->两张表基础上延伸出来第三张表
    ->两张表双向的foreginkey
    ->用第三张表来保存双向关系

    u2g=models.ManyToManyField('UserGroup')

    3.4 多表字段的参数

    3.4.1 基本参数

    to                 #关联的表
    to_field           #关联的字段
    related_name       #可以将xxx_set重命名为 新的名字 xxx_set==a
    related_query_name #可以将xxx重命名为新的民资  如b_set==xxx_set

    3.4.2 on_delete的参数

    在一对多关联情况下我们进行删除操作,假如有两张表,一张用户表,一张用户类型表, 我们在删除用户类型表中的数据的时候,由于关联关系的存在,原始sql会报错。

    在django中 models.UserType.objects.filter(id=1).delete()

    早期django会报错

    现在django,我们可以定义on_delete()参数来设定在删除关联表的时候,django对关联数据进行的操作。

    models.CASCADE,     #删除关联数据,与之关联也删除
    models.DO_NOTHING,  #删除关联数据,引发错误IntegrityError 数据库抛出异常
    models.PROTECT,     #删除关联数据,引发错误ProtectedError django抛出异常
    models.SET_NULL,    #删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
    models.SET_DEFAULT, #删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
    models.SET,         #删除关联数据,
                             #a. 值            与之关联的值设置为指定值,设置:models.SET(值)
                             #b. 函数(返回值)  与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

    4、数据库基本操作

    4.1 增加(两种)

    #method 1
    models.Tb1.objects.create(c1='xx', c2='oo')      #增加一条数据,可以接受字典类型数据 **kwargs
    #method 2
    obj = models.Tb1(c1='xx', c2='oo')
    obj.save()

    4.2 删除

    models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据

    4.3 修改

    models.Tb1.objects.filter(name='seven').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
    obj = models.Tb1.objects.get(id=1)
    obj.c1 = '111'
    obj.save()                                                 # 修改单条数据

    4.4 查找

    models.Tb1.objects.get(id=123)         # 获取单条数据,不存在则报错(不建议)
    models.Tb1.objects.all()               # 获取全部
    models.Tb1.objects.filter(name='seven') # 获取指定条件的数据
    models.Tb1.objects.exclude(name='seven') # 排除指定条件的数据

    5、数据库连表操作的基本操作

    5.1 一对多的正向操作

    5.1.1 通过.(点)来获取对象

    5.1.2 通过双下划线来获取对象

    s4=models.dev_info.objects.filter(did__lt=5)
    s5=models.dev_info.objects.filter(did__lt=5).values('dev_ip','dev_port','dev_type__type_name')
    s6=models.dev_info.objects.filter(did__lt=6).values_list('dev_ip','dev_port','dev_type__type_name')

    5.2 一对多的反向操作

    (注意,自关联时候,related_name、related_query_name 参数)

    5.2.1 通过'.+classname+_+set'来操作

    s7 = models.devtype_table.objects.all()
    for i in s7:
        print(i.type_name,i.dev_info_set.all())
    -----------------------------------------
    路由器 <QuerySet [<dev_info: dev_info object (3)>, <dev_info: dev_info object (12)>, <dev_info: dev_info object (21)>, <dev_info: dev_info object (23)>]>
    交换机 <QuerySet [<dev_info: dev_info object (5)>, <dev_info: dev_info object (17)>]>
    防火墙 <QuerySet [<dev_info: dev_info object (6)>]>
    服务器 <QuerySet [<dev_info: dev_info object (4)>, <dev_info: dev_info object (11)>]>

    5.2.2 通过双下划线来操作,双下划线时候不需要set

    s8=models.devtype_table.objects.all().values('type_name','dev_info__dev_ip','dev_info__dev_port')
    for i in s8:
         print(i)
    -----------------------------------------------------------------
    {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.1', 'dev_info__dev_port': '44568'}
    {'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': '666'}
    {'type_name': '路由器', 'dev_info__dev_ip': '11122', 'dev_info__dev_port': '1111'}
    {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': '123'}
    {'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': '80'}
    {'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': '200'}
    {'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': '443'}
    {'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': '80'}
    {'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': '1444'}

    5.3 多对多的正向操作

    5.3.1 通过.(点)来获取对象

    all/filter/remove/clear/set/ 可以操作id,可以操作对象

    #all
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup obj
    ect (3)>]>
    #filter
    >>> x.u2g.filter()
    <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup obj
    ect (3)>]>
    #delete不生效
    >>> x.u2g.delete()
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
    AttributeError: 'ManyRelatedManager' object has no attribute 'delete'
    #clear
    >>> x.u2g.clear()
    >>> x.u2g.all()
    <QuerySet []>
    #set
    >>> x.u2g.set([1,2,3])
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]>
    #remove
    >>> x.u2g.remove(1)
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]>
    #add 可以添加对象
    >>> y=models.UserGroup.objects.get(id=1)
    <UserGroup: UserGroup object (1)>
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]>
    >>> x.u2g.add(y)
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]>
    #remove 可以删除对象
    >>> x.u2g.remove(y)
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]>
    #set可以设置对象,但是需要添加进列表
    >>> x.u2g.set(y)
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "C:Python36libsite-packagesdjangodbmodelsfields
    elated_d
    escriptors.py", line 975, in set
        objs = tuple(objs)
    TypeError: 'UserGroup' object is not iterable
    >>> z=[y,]
    >>> x.u2g.set(z)
    >>> x.u2g.all()
    <QuerySet [<UserGroup: UserGroup object (1)>]>

    5.3.2 通过双下划线来获取对象

    和foreginkey一样,此处不展开,需要注意values_list,values操作是对queryset进行的操做

    5.4 多对多的反向操作

    5.4.1 通过.点set操作

    x=models.UserGroup.objects.get(id=3)
    >>> x.userinfo_set.all()
    <QuerySet [<UserInfo: UserInfo object (3)>, <UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]>

    5.4.2 通过双下划线操作

    >>> i=models.UserGroup.objects.filter(id=1)
    >>> i
    <QuerySet [<UserGroup: UserGroup object (1)>]>
    >>> i.values_list()
    <QuerySet [(1, 'CEO')]>
    >>> i.values_list('id')
    <QuerySet [(1,)]>
    >>> i.values_list('id','groupname')
    <QuerySet [(1, 'CEO')]>
    >>> i.values_list('id','groupname','userinfo__id')
    <QuerySet [(1, 'CEO', 3), (1, 'CEO', 1)]>
    >>> i.values_list('id','groupname','userinfo__name')
    <QuerySet [(1, 'CEO', 'xxx1'), (1, 'CEO', 'liuliuliu')]>

    6、queryset对象的常用操作

    #取值
    all()
    exclude()
    filter()
    #排序
    order_by()
    reverse()倒叙,需要和order_by结合使用
    #删除
    delete()
    #分组、去重
    annotate()分组使用
    distinct()去重 只能psql用
    #获取对象的部分信息 类似values。values_list 但是取出的是queryset
    defer 一次sql不取哪几列,如果写这几列,还会发请求取出
    only 一次sql只取哪几列,如果取其他的列,还会发请求取出

    7、queryset中和性能相关的两个重要参数

    7.1 select_related

    select_related 方法在查询的时候把与之关联的表所有的都拿过来了

    我们可以指定只拿相关联的表的某个字段

    select_related('foreginkey')

    只拿这个foreginkey字段

    models.xxx.objects.all().select_related()

    7.2 prefetch_related 分步查询

    其流程是先查单张表,然后查出有多表的字段的取值范围,再去关联表中取出在这个取值范围内的数据

    users=models.User.objects.filter(id__gt=30).prefetch_related('ut','tu')
    #step1 select * from users where id > 30
    #step2 获取上一步骤中所有的ut_id=[1,2]
    # select * from user_type where id in [1,2]

    8、数据库的补充操作

    8.1 运行原生sql语句的raw方法

    #raw原生sql
    models.xxx.objects.raw('select * from from tb') #会转换为queryset
    如果表结果不对
    models.xxx.objects.raw('select nid as id ,xxname as name from from tb') #会转换为queryset
    get_or_create(username='root',default={'email'='123@123.cn','pwd'=12345})    
    update_or_create()
    exists()

    8.2 其他的一些补充

    # 获取个数
    #
    # models.Tb1.objects.filter(name='seven').count()
    
    # 大于,小于
    #
    # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
    # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
    # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
    # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
    # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
    
    # in
    #
    # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
    # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
    
    # isnull
    # Entry.objects.filter(pub_date__isnull=True)
    
    # contains
    # 
    # models.Tb1.objects.filter(name__contains="ven") 相当于like
    # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
    # models.Tb1.objects.exclude(name__icontains="ven")
    
    # range
    #
    # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
    
    # 其他类似
    #
    # startswith,istartswith, endswith, iendswith,
    
    # order by
    #
    # models.Tb1.objects.filter(name='seven').order_by('id') # asc
    # models.Tb1.objects.filter(name='seven').order_by('-id') # desc 可以支持多个参数 逐个排序
    
    # group by
    #
    # from django.db.models import Count, Min, Max, Sum
    # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
    # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
    
    # limit 、offset
    #
    # models.Tb1.objects.all()[10:20]
    
    # regex正则匹配,iregex 不区分大小写
    #
    # Entry.objects.get(title__regex=r'^(An?|The) +')
    # Entry.objects.get(title__iregex=r'^(an?|the) +')
    
    # date
    #
    # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
    # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
    
    # year
    #
    # Entry.objects.filter(pub_date__year=2005)
    # Entry.objects.filter(pub_date__year__gte=2005)
    
    # month
    #
    # Entry.objects.filter(pub_date__month=12)
    # Entry.objects.filter(pub_date__month__gte=6)
    
    # day
    #
    # Entry.objects.filter(pub_date__day=3)
    # Entry.objects.filter(pub_date__day__gte=3)
    
    # week_day
    #
    # Entry.objects.filter(pub_date__week_day=2)
    # Entry.objects.filter(pub_date__week_day__gte=2)
    
    # hour
    #
    # Event.objects.filter(timestamp__hour=23)
    # Event.objects.filter(time__hour=5)
    # Event.objects.filter(timestamp__hour__gte=12)
    
    # minute
    #
    # Event.objects.filter(timestamp__minute=29)
    # Event.objects.filter(time__minute=46)
    # Event.objects.filter(timestamp__minute__gte=29)
    
    # second
    #
    # Event.objects.filter(timestamp__second=31)
    # Event.objects.filter(time__second=2)
    # Event.objects.filter(timestamp__second__gte=31)
    
    # extra
        #
        # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
        #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
        #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
        #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
        #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
    
        # F
        #
        # from django.db.models import F
        # models.Tb1.objects.update(num=F('num')+1)
    
    
        # Q
        #
        # 方式一:
        # Q(nid__gt=10)
        # Q(nid=8) | Q(nid__gt=10)
        # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        # 方式二:
        # con = Q()
        # q1 = Q()
        # q1.connector = 'OR'
        # q1.children.append(('id', 1))
        # q1.children.append(('id', 10))
        # q1.children.append(('id', 9))
        # q2 = Q()
        # q2.connector = 'OR'
        # q2.children.append(('c1', 1))
        # q2.children.append(('c1', 10))
        # q2.children.append(('c1', 9))
        # con.add(q1, 'AND')
        # con.add(q2, 'AND')
        #
        # models.Tb1.objects.filter(con)
    
    
        # 执行原生SQL
        #
        # from django.db import connection, connections
        # cursor = connection.cursor()  # cursor = connections['default'].cursor()
        # cursor.execute("""SELECT * from auth_user where id = %s""", [1])
        # row = cursor.fetchone()

     9、数据验证(弱)

    models主要是用来做数据库的相关操作,对于数据验证,功能比较薄弱,建议和form搭配,使用form的数据验证功能

    9.1 models的数据验证有full_clean()方法

    full_clean()方法有两个部分,

    第一个部分是验证字段的正则表达式,是否匹配

    第二部分是给了一个self.clean()的钩子

    9.2 self.clean()钩子函数

    我们可以通过self.clean()钩子对数据进行验证。

    验证成功则可以执行obj.save()方法,否则程序报错。
    models的验证功能建议用form实现

     

    二、form深入

    1、form的功能

    form强大的数据验证功能

    2、form处理的流程

    前端发来的数据->form挡一层->放入数据库

    3、form基本功能(验证+标签生成)

    3.1 is_valid()验证(与models中的full_clean方法对比,full_clean正则表达式,定义clean方法全局二次验证)
    3.2 cleaned_data获取验证后的数据
    3.3 生成html as_json as_p as_table

    4、form的操作--阶段一

    4.1 单独建立一个forms.py文件 独立开来便于管理
    4.2 类中继承forms.Form

    4.3 name=fields.charField()定义字段并设置参数
        4.3.1 生成html 默认input
        4.3.2 widget=widget.Textarea(attrs={})定义插件和属性
        4.3.3 request=True 是否必填限制
        4.3.4 max_length=32 长度限制
        4.3.5 pwd=fields.charField()
        4.3.6widget=widgets.PasswordInput(attr={'class':'c1'})
    4.4 综上结论
        4.4.1 字段用来验证数据
        4.4.2 widget插件用来生成html标签
    4.5 考虑
        我们是否需要用form的所有功能?
        4.5.1 验证 (一定用)x=LoginForm(request.POST)
        4.5.2 生成html(我们可以自己定义html,并交给form验证)

        假如:

        ->新url的方式提交数据 建议使用form来生成html 这样可以达到保留数据的作用    -> ajax方式提交数据,可以不使用form生成的html,我们可以自己写,但是,同样我们依然能使用form生成

    5、form操作--阶段二 (select标签的选项的处理)

     

    上一篇博客已经写到了form中widget的各种选项插件了,此处不做插件说明。

    5.1 手写选项

    usertype=fields.choicesField(widget=widgets.SELECT,choices=((0,'超管'),(1,'普通用户'))

    5.2 数据库中获取

     

    choices=models.usertype.objects.value_list('id','name')
    usertype=fields.choicesField(widget=widgets.SELECT,choices=choices)

    5.3 新增字段后需要重启服务的解决方法(2种)

        5.3.1 __init__()方法*(重点方法)

    class user3(forms.Form):
        def __init__(self,*args,**kwargs):
            super(user3, self).__init__(*args,**kwargs)
            self.fields['user_type'].widget.choices=models.user_type.objects.values_list('id','type')
        name=fields.CharField(max_length=32)
        user_type=fields.CharField(widget=widgets.Select(choices=[],)
        )

    5.3.2 使用django 自带的modlechoiceField(只做了解)

    6、form操作--阶段三

    6.1 初始化操作 ->传入字典

     

    6.2 数据验证及钩子过程

    6.2.1 正则表达式验证(django字典)

    6.2.2 form无法对数据库中是否存在此字段进行验证所以需要使用form自带钩子

    6.2.3 源码实现流程 

    is_valid() -> self.errors() -> self.full_clean()(models中也有full_clean)

    6.2.4 源码的几个钩子

    注意区别->
    self._clean_fields()
    self._clean_form()
    self._post_clean()

     

    6.3 _clean_fields 源码分析

    6.3.1 先过正则表达式验证

    6.3.2 hasattr(self, 'clean_%s' % name)

    6.3.3 value = getattr(self, 'clean_%s' % name)()

    6.3.4 return value

    6.3.5  self.cleaned_data[name] = value

     

    6.3.6 返回正确的值

    6.3.7 raise错误的方法

    def clean_email(self):
        raise ValidationError('测试raise错误',code=250)



    6.4 _clean_form 源码分析

    6.4.1 self.clean()

        def clean(self):
            """
            Hook for doing any extra form-wide cleaning after Field.clean() has been
            called on every field. Any ValidationError raised by this method will
            not be associated with a particular field; it will have a special-case
            association with the field named '__all__'.
            """
            return self.cleaned_data

    6.4.2 对整体进行验证抛出异常 __all__的处理 ('__all__':[] #NONE_CLEANED_FIELDS)



    ->form自定义正则表达式验证 fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[])

     6.5 _post_clean源码

    6.6 form自定义正则表达式验证的方法

    form自定义正则表达式验证   fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[])

    6.7 form验证的完整执行顺序

    6.7.1 正则表达式

    6.7.2 validators

    6.7.3 clean_%s

    6.7.2 clean 

    6.7.5 post_clean

    6.7.6 成功信息 obj.cleaned_data

    6.7.7 错误信息

    Obj.errors[
    '__all__':[] #NONE_CLEANED_FIELDS
    'user':[{'code':xxx,'messages':xxx}],
    'pwd':[{'code':xxx,'messages':xxx}],
    ]

    7、实例 ajax请求 进行登录的返回值处理

    需要考虑一,验证失败,objects.errors是一个ValidationError,如何处理

    需要考虑的情况二,验证成功,要去数据库中取值,queryset对象如何序列号,queryset对象内的时间格式如何序列化

    7.1 情况一,验证失败,ValidationError的处理方式(2种方式)

    obj.errors是一个errordict ,要返回ajax 需要对obj.errors 进行序列化处理

    7.1.1 思路一 obj.errors.as_json 双方进行两次json

    <script>
        $(function () {
            $('#button').click(
            function () {
              $.ajax({
                  url:'{{ request.path_info }}',
                  type:'POST',
                  data:$('#auth_form').serialize(),
                  success:function (r_data) {
                      dict=JSON.parse(r_data);
                      console.log(dict);
                      err_dict=JSON.parse(dict['error']);
                      console.log(err_dict);
    
                  },
                  error:function () {
                      pass
                  }
              })
            }
        )
        })
    </script>
    
    
    def login(request):
        ret={'status':True,'error':None,'data':None}
        if request.method=="GET":
            return render(request,'l4/login.html')
        if request.method=="POST":
            obj=login_form(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
            else:
                print(obj.errors)
                ret['error']= obj.errors.as_json() #第一次json
    return HttpResponse(json.dumps(ret))  #第二次json

    7.1.2 思路二 obj.errors.as_data {'key':validators}

    原理:json.dumps()时我们可以加入参数cls,json在dumps的过程中会执行cls的default方法

    class newdumps(json.JSONEncoder):
        def default(self, fields):
            if isinstance(fields,ValidationError):
                return {'code':fields.code,'messages':fields.messages}
            else:
                return json.JSONEncoder.default(self,fields)
    def login(request):
        ret={'status':True,'error':None,'data':None}
        if request.method=="GET":
            return render(request,'l4/login.html')
        if request.method=="POST":
            obj=login_form(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
            else:
                print(obj.errors)
                ret['error']= obj.errors.as_data()
        return HttpResponse(json.dumps(ret,cls=newdumps))

    7.2 情况二 验证成功,要去数据库中取值,queryset对象如何序列号,queryset对象内的时间格式如何序列化(2种方法)

    7.2.1 django内置的序列号方法

     

    from django.core import serializers
    v = models.tb.objects.all()
    data = serializers.serialize("json", v)

     

    7.2.2 自定义cls来自定制json.dumps

                    import json 
                    from datetime import date 
                    from datetime import datetime 
                       
                    class JsonCustomEncoder(json.JSONEncoder): 
                        
                        def default(self, field): 
                         
                            if isinstance(field, datetime): 
                                return field.strftime('%Y-%m-%d %H:%M:%S') 
                            elif isinstance(field, date): 
                                return field.strftime('%Y-%m-%d') 
                            else: 
                                return json.JSONEncoder.default(self, field) 
       
                    v = models.tb.objects.values('id','name','ctime')
                    v = list(v)
                    v = json.dumps(v,cls=JsonCustomEncoder) ->datetime 还是需要处理

    同样还是重构default方法,Python 中的isinstance函数,isinstance是Python中的一个内建函数。是用来判断一个对象的变量类型。

     三、modelform

    1、modelform的概括

    modelform顾名思义有以下个方面的内容

    1.1 数据库操作(表示其具有model的性质)

    1.2 字段验证(表示其具有form的特性)

    1.3 生成html(表示其具有form的特性)

    2、 由model+form引出modelform的操作

    2.1 model+form 添加或修改一条数据库内容的方式

    2.1.1 model定义表,创建表

    2.1.2 form定义对象

    2.1.3 form在界面生成相应html标签

    2.1.4 request返回后,进行is_vaild()验证

    2.1.5 验证通过后生成cleaned_data交给model

    2.1.6 model进行create或update操作

     

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from django import forms
    
    from django.forms import fields
    from django.forms import widgets
    from app01 import models
    class UserInfo(forms.Form):
        username=fields.CharField(max_length=32)
        email=fields.EmailField()
        usertype_id=fields.ChoiceField(
            choices=[],
            widget=widgets.Select
        )
    
        def __init__(self,*args,**kwargs):
            super(UserInfo, self).__init__(*args,**kwargs)
            self.fields['usertype_id'].choices=models.UserType.objects.values_list('id','caption')
    
    def login(request):
        if request.method=="GET":
          obj=UserInfo()
          print(obj.fields)
          return render(request,'login.html',{'obj':obj})
        if request.method=="POST":
          obj=UserInfo(request.POST)
          if obj.is_valid():
             res=obj.cleaned_data
             print(res)
             models.UserInfo.objects.create(**res)
           # models.UserInfo.objects.filter(id=1).update(**res)
             return HttpResponse('ok')
          else:
             print('xxxxx',obj.errors)
             return HttpResponse('no')
    
    def test(request):
        return render(request,'1.html')
    **************************
    <body>
    <form action="/p1/login" method="post">
      {% csrf_token %}
      {{ obj.as_p }}
      <input type="submit" value="提交">
    </form>
    </body>
    ******************
    from django.db import models
    # Create your models here.
    class UserType(models.Model):
        caption=models.CharField(max_length=32)
    class UserInfo(models.Model):
        username=models.CharField(max_length=32)
        email=models.EmailField()
        usertype=models.ForeignKey(to=UserType,to_field='id',on_delete=models.CASCADE)
    ******************

     

    2.2 添加或修改一条数据库内容的方式

    2.2.1 model定义表、创建表

    2.2.2 定义modelform,继承model的表的字段

    2.2.3 modelform界面生成html

    2.2.4 request返回后,modelform直接is_vaild()

    2.2.5 modelform直接save()保存

    数据库的操作直接被封装起来了。同时,如果在取数据库记录的时候,相应的字段关联,也都会被自动取出。

    现在,我们展开说modelform。

    3、定义基本的modelform

    class MYModelForm(forms.ModelForm)
            class Meta:
                   model=models.UserInfo
                   fields='__all__'

    此时,html就已经定义好了。

    def login2(request):
        if request.method=="GET":
          obj=UserInfo_modelform()
          return render(request,'login2.html',{'obj':obj})
    ---------------------------------
    class UserInfo(models.Model):
        username=models.CharField(max_length=32)
        email=models.EmailField()
        usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE)
        u2g=models.ManyToManyField('UserGroup')
    -------------------------
    class UserInfo_modelform(forms.ModelForm):
        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__' #需要展示哪些字段
    ---------------------------------------
    <body>
    <form action="/p1/login2" method="post">
      {% csrf_token %}
      {{ obj.as_p }}
      <input type="submit" value="提交">
    </form>
    </body>

    通过对比,我们可以发现,foreignkey及manytomany字段,modelform生成的html自动关联了相关数据

    相比于form生成html,此处简便了非常多的操作

    4、modelform中class Meta的一些参数

    4.1 model #与之关联的model

    4.2 fields=None, #显示哪些字段 

    class UserInfo_modelform(forms.ModelForm):
        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields=['username'] #可以是列表

    4.3 exclude #排除哪些字段 

    class UserInfo_modelform(forms.ModelForm):
        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            exclude=['username']

    4.4 verbose与label 显示中文

      class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__'
            labels={
                'username':'用户名',
                'u2g':'所属组'
            }
    -----------------------------
    class UserInfo(models.Model):
        username=models.CharField(max_length=32)
        email=models.EmailField(verbose_name='邮箱')
        usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE)
        u2g=models.ManyToManyField('UserGroup')

    4.4 help_text 帮助信息

        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__'
            labels={
                'username':'用户名',
                'u2g':'所属组'
            }
            help_texts={
                'email':'xxx@xxx.com'
            }

    4.5 插件定制 widgets 

    自定义插件 form中的插件,需要注意此处widgets与插件的widgets重名的情况 *

        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__'
            labels={
                'username':'用户名',
                'u2g':'所属组'
            }
            help_texts={
                'email':'xxx@xxx.com'
            }
    
            widgets={
               'username':Fwidgers.Textarea(attrs={'class':'c1'})
            }

     4.6 error_messages 自定义错误信息

    自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)

     

        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__'
            #fields=['username'] #可以是列表
            #exclude=['username']
            labels={
                'username':'用户名',
                'u2g':'所属组'
            }
            help_texts={
                'email':'xxx@xxx.com'
            }
    
            widgets={
               'username':Fwidgers.Textarea(attrs={'class':'c1'})
            }
            error_messages = {
                'username':
                    {
                        'max_length':"this writer name is too long"
                    }
            }

     

    4.7 field_classes=None # 自定义字段类 (也可以自定义字段) 将邮箱格式定义为url格式

    4.8 localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 默认utc时间

    4.9 定义models中没有的字段(一些不需要入数据库的字段,如一个月免登陆,等等)

    class UserInfo_modelform(forms.ModelForm):
        extra_input=Ffields.CharField(max_length=32)
        class Meta:
            model=models.UserInfo #去哪个类里面获取字段
            fields='__all__'
            #fields=['username'] #可以是列表
            #exclude=['username']
            labels={
                'username':'用户名',
                'u2g':'所属组'
            }
            help_texts={
                'email':'xxx@xxx.com'
            }
    
            widgets={
               'username':Fwidgers.Textarea(attrs={'class':'c1'})
            }
            error_messages = {
                'username':
                    {
                        'max_length':"this writer name is too long"
                    }
            }

    以上,生成html标签,样式的方式全部完毕

    5、数据验证及保存

    5.1 modelform与form的关系及数据验证的原理

                         Form->BaseForm-> is_valid 
    modelform->Base ModelForm->BaseForm-> is_vaild

    is_vaild()是BaseForm的方法,所以modelform和form都有is_valid()方法。

    5.2 modelform的数据保存

    if obj.is_valid(): 
        obj.save()
    #直接跳过models的操作 保存了数据
    #一对多 多对多 直接帮我们跳过了所有的关联操作

    6、modelform的钩子

    与form相同,modelform的钩子函数与form一样,故modelform中也有3个钩子函数用来自定义数据验证(钩子函数写在modelform中)

     7、数据的编辑与更新 instance的使用

    ->显示数据库数据直接将model对象放入 instance=model.obj

    ->更新 instance参数 obj=UserInfo_modelform(request.POST,instance=t_o)

    def userdetail(request,nid):
        if request.method=="GET":
          obj=models.UserInfo.objects.filter(id=nid).first()
          mobj=UserInfo_modelform(instance=obj)
          return render(request,'userdetail.html',{'obj':mobj})
        if request.method=="POST":
            t_o = models.UserInfo.objects.filter(id=nid).first()
            obj=UserInfo_modelform(request.POST,instance=t_o)
            if obj.is_valid():
                obj.save()
                return redirect('/p1/userdetail-%s.html'%nid)
            else:
                return render(request,'/p1/userdetail-%s.html'%nid,obj)

    8、obj.save(false)的理解

    modelform为我们封装了很多数据库数据保存的操作。包括单表和多表,通过一个save都可以完成。

    save()保存实际是包括了两个步骤

    self.instance.save() #保存单表

    self._save_m2m() #保存多表

     

         if obj.is_valid():
             #obj.save()
             x=obj.save(False)
             x.save()
             x.save_m2m()

    9、回顾modelform流程

    9.1 生成html class Meta

    9.2 mf=xxxmodelform(instance=)

    9.3 额外标签

    9.4 各种验证 钩子 is_valid()

    9.5 保存 save()

    9.6 拆开

        ->saveinstance.save()

        ->mf.save_m2m()

    四、原生ajax

    1、背景

    1.1 jquery中的ajax是对xhr(xmlHttpRequest)对象的封装

    1.2 xhr对象在老版本的ie中不存在,当然,老版本ie中有类似的对象,在2.x/3.x版本的jquery中,不再支持老版本的ie,所以需要注意

    2、原生ajax的理解

    2.1 从socket角度理解原生ajax 

    ->1、建立对象
       b = new XMLHttpRequest()
    ->2、建立连接 open
       b.open(method,url,async)
       连接时所需要信息
       ->method POST、GET、DELETE...
       ->url   
       ->async 同步还是异步(true为异步)
    ->3、发送内容 send() str
    ->4、设置请求头
        ->set RequestHeader('key','value')
    ->5、获取所有响应头的内容
        ->getALLResponseHeaders()
    ->6、获取某一个响应头的内容
        ->getResponseHeader('key')
    ->7、abort()终止请求

    2.2 从jquery $.ajax角度理解原生ajax

    ->$ajax(
    {url
     method
     data
     success 
    }
    )        
    ->new
    ->open 
    ->send
    ->get

    3、原生ajax的基本操作一

    建立连接,发送数据

    b = new xmlHttpRequest()
    b.open('GET','http://127.0.0.1:8000',true)
    b.send('name=root,pwd='pwd')

    print(request.POST)

    4、原生ajax的基本操作二

     4.1 理解状态码 b.readstatus

    0-未初始化,尚未调用open()方法;
    1-启动,调用了open()方法,未调用send()方法;
    2-发送,已经调用了send()方法,未接收到响应;
    3-接收,已经接收到部分响应数据;
    4-完成,已经接收到全部响应数据;

     4.2 理解回调函数 onreadystatechange

    onreadystatechange=function(){}

    当readState每次发生变化的时候执行此函数
    if (b.readState == 4){} 做条件

    4.3 理解获取返回值的方法  

    responseText 主要用Text 字符串数据 包括json数据
    responseXML

    4.4 知道状态码  b.states与b.statesText

     

    b.status
    404
    b.readyState
    4
    b.responseText
    "<!DOCTYPE html>
    <html lang="en">
    <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <title>Page not found at /index</title>
      <meta name="robots" content="NONE,NOARCHIVE">
      <style type="text/css">
        省略
      </style>
    </head>
    <body>
      省略
    </body>
    </html>
    "
    b.responseType
    ""
    b.statusText
    "Not Found"   ->使用 return Httpresponse(‘xxx’,status=404,reason='Not Found'

     

    补充httpresponse设置返回值的方法

     

    def test(request):
        return HttpResponse('ok',status=404,reason='Not Found')

     

     

     

    4.5 知道还原json数据的方法 JSON.parse(b.responseText) 

    x=[1,2,3]
    (3) [1, 2, 3]
    y=JSON.stringify(x);
    "[1,2,3]"
    JSON.parse(y);
    (3) [1, 2, 3]

    4.6 知道如何获取响应头 getResponseHeader 

    b.open('GET','/p1/userdetail-3.html',true)
    b.send()
    b.getResponseHeader('cookie')
    null
    b.getResponseHeader('Content-Type')
    "text/html; charset=utf-8"

    4.7 知道如何设置请求头

     setRequestHeader('csrfToken','value') #csrf    


    4.8 理解post动作的处理

    使用xhr发送post数据的时候需要设置请求头Content-Type

     

        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset-UTF-8')

     

    4.9 ie兼容性处理的方法

     function GetXHR(){
                var xhr = null;
                if(XMLHttpRequest){
                    xhr = new XMLHttpRequest();
                }else{
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
                return xhr;
    
            }

    4.10 $.ajax()中获取xhr对象的方法

    ->$.ajax(
    {
    success:function(data,x1,,x2) 
    }
    )

    五、伪ajax

    1、理解一 iframe标签的使用

    iframe发送请求的方法

    <body>
    <div>iframe测试</div>
    <iframe id='my_iframe' src="https://www.baidu.com"></iframe>
    <div><input id="input_url"></div>
    <div><input id='iframe_button' type="button" value="提交iframe"></div>
    <script src="jquery-1.12.4.js"></script>
    <script>

    jquery

    $('#iframe_button').click(function () {
    var tmp_url = $('#input_url').val();
    $('#my_iframe').attr('src',tmp_url);
    })
    </script>
    </body>

    iframe可以通过src发送get请求且界面不刷新

    2、理解二:利用iframe来提交表单

    2.1 在form内添加一个iframe标签

    2.2 给form标签添加一个target的属性 target=‘iframename’

    2.3 form的method可以是get也可以是post都可以

    2.4 submit这个form表单即可

     post提交

     <form action="/p1/xml_ajax" method="post" target="ifm1">
         <iframe name="ifm1" id="ifm1"></iframe>
         <input type="text" name="name">
         <input type="text" name="passwd">
         <input  type="submit">
     </form>

    get 提交

     <form action="/p1/xml_ajax" method="get" target="ifm1">
         <iframe name="ifm1" id="ifm1"></iframe>
         <input type="text" name="name">
         <input type="text" name="passwd">
         <input  type="submit">
     </form>

    def xml_ajax(request):
        res={'state':True,'err':None,'data':None}
        if request.method=='GET':
            return HttpResponse('iframe-get')
        if request.method=="POST":
            print(request.POST)
            res['data']='ok'
            return HttpResponse(json.dumps(res))

    3、理解三 :获取iframe的返回内容

    3.1 iframe的onload事件的理解

    对端返回完成后 触发onload

    3.2 iframe返回值所存放的位置

    3.3 获取document对象的方法

    jquery

    $('#ifm1').contents().find('body').text();    

    JavaScript

    document.getElementById('ifm1').contentWindow.document.body.innerHTML

    实例

    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        $('#ifm1').load(function () {
           console.log($('#ifm1').contents().find('body').text());
            var tmp_text=$('#ifm1').contents().find('body').text();
           var t_obj = JSON.parse(tmp_text);
            console.log(t_obj)
        })
    </script>

    4、发送普通数据(string)时,三种ajax方式的对比

    普通数据:jquery>xmlHttpRequest->iframe 

    普通数据时,jquery是最方便最优先的方式


    六、多种ajax对上传文件的处理

    1、定义一个美观的上传界面的方法 (用一个button来覆盖住input file)

     <div style="position: relative;height: 60px; 100px;" >
         <input type="file" id='fafafa' style="height: 60px; 100px; z-index: 90;position: absolute;top:0;bottom: 0;right: 0;left: 0;opacity: 0">
         <a style="border-radius: 20px;text-align: center;line-height: 60px;z-index: 50;height: 60px; 100px;background-color: deepskyblue;border: 1px solid darkblue;position: absolute;top:0;bottom: 0;right: 0;left: 0">上传</a>
     </div>

    2、获取<input type='file'>的文件对象

    var fobj=document.getElementById('fafafa').files[0]
    f.obj.name//可以获取文件名

    3、原生xhr发送文件

    3.1 依赖FormData()对象 FormDate().append(key,value)
    3.2 不需要添加POST头

     <input type="file" id="input_file">
      <input type="button" value="XmlHttpRequest发送" id="xhr_button">
    -----------------------------
     $('#xhr_button').click(
            function () {
                var file_obj=document.getElementById('input_file').files[0];
                var xhr_connect=new XMLHttpRequest();
                xhr_connect.open('POST','/p1/receive_file',true);
                var tmp_data=new FormData();
                tmp_data.append('k1','123');
                tmp_data.append('realfile',file_obj);
                xhr_connect.onreadystatechange=function () {
                    if (xhr_connect.readyState===4){
                        console.log(xhr_connect.responseText);
                    }
                };
                xhr_connect.send(tmp_data);
            }
        );

    $('#xhr_button').click(
            function () {
                var file_obj=document.getElementById('input_file').files[0];
                var xhr_connect=new XMLHttpRequest();
                xhr_connect.open('POST','/p1/receive_file',true);
                var tmp_data=new FormData();
                tmp_data.append('k1','123');
                tmp_data.append('csrfmiddlewaretoken','{{ csrf_token }}');
                tmp_data.append('realfile',file_obj);
                xhr_connect.onreadystatechange=function () {
                    if (xhr_connect.readyState===4){
                        console.log(xhr_connect.responseText);
                    }
                };
                xhr_connect.send(tmp_data);
            }
        );

    formdata不仅能携带文件,还能发送参数。

    4、jquery ajax发送文件

    ->同样依赖FormDatad对象
    ->添加两个参数
    processData:false, // tell jquery not to process the data
    contentType:false, //tell jquery not to set contentTYPE
    $.ajax({
    data:f_obj
    processData:false, // tell jquery not to process the data
    contentType:false, //tell jquery not to set contentTYPE
    })

    $('#ajax_button').click(function () {
             var file_obj=document.getElementById('input_file').files[0];
             var tmp_data=new FormData();
             tmp_data.append('k1','123');
             tmp_data.append('csrfmiddlewaretoken','{{ csrf_token }}');
             tmp_data.append('realfile',file_obj);
             $.ajax(
                 {
                     url:'/p1/receive_file',
                     type:'POST',
                     data:tmp_data,
                     processData:false, // tell jquery not to process the data
                     contentType:false, //tell jquery not to set contentTYPE
                     success:function (receive_data,a1,a2) {
                         console.log(receive_data);
                         console.log(a1);
                         console.log(a2);
                     }
                 }
             )
        });


    注:formdata在ie浏览器中不兼容


    5、iframe 发送文件

    form 中 enctype="multipart/form-data 添加<input type='file'>其他和发普通请求完全一致

      <form action="/p1/receive_file" method="post" target="ifm1" enctype="multipart/form-data">
         {% csrf_token %}
          <iframe name="ifm1" id="ifm1" style="display: none"></iframe>
         <input type="text" value="123" name="k1" style="display: none">
         <input type="file" name="realfile">
         <input type="submit" id="iframe_button" value="iframe提交">
      </form>
    -------------------------------------  
    
      $('#iframe_button').click(
            function () {
             $('#ifm1').load(
                 function () {
                     console.log('iframe_ok')
                 }
             );
            }
        );

    6、ajax文件传输归纳

    由于formdata对ie浏览器不支持,所以ajax传文件的优先级为

    iframe>jquery>原生ajax

    7、图片预览

    7.1 input的file标签onload触发 iframe的submit

    7.2  返回值 $('iframename').contents().find('body').text()获取图片path

    7.3 在相应位置插入图片

    var img_obj=document.createElement('img');
    img_obj.src=tmp_path;
    $('#pre_view').empty().append(img_obj);

     

      <form action="/p1/receive_pic" method="post" target="ifm2" enctype="multipart/form-data" id="pic_form">
         {% csrf_token %}
          <iframe name="ifm2" id="ifm2" style="display: none"></iframe>
         <input type="file" name="user_pic" id="user_pic">
      </form>
    --------------------------------
    $('#ifm2').load(function () {
            tmp_path=$('#ifm2').contents().find('body').text();
            tmp_path='/'+tmp_path;
            var img_obj=document.createElement('img');
            img_obj.src=tmp_path;
            //img_obj.style.width='200px';
            //img_obj.style.height='400px';
            $('#pre_view').empty().append(img_obj);
        });
    --------------------------------
    def receive_pic(request):
        if request.method=="POST":
            y=request.FILES.get('user_pic')
            y_name=str(y.name)
            pic_path=os.path.join('static/image/',y.name)
            print(pic_path)
            f=open(pic_path,'wb')
            for i in y.chunks():
                f.write(i)
            return HttpResponse(pic_path)

     

     七、随机验证码生成

    1、分析

    1.1 一个url显示网页框架,一个url用来显示图片给img标签

    1.2 不同的人,不同的验证码,验证码需要和session相关联

    1.3 使用一函数,生成图片和验证码内容,每次单击图片,更新图片及更新session

    1.4 用户post登录,验证输入的验证码和session中的验证码是否一致

    1.5 img图片只有修改路径后才会刷新,如何解决。

    2、实现

    2.1 pillow函数生成图片及字符串,我们可以将图片保存在本地并rb后Httpresponse给前方,或者直接写在BIOSIO中,省去保存图片到本地的操作,此过程实现细节可以不做深入,直接调用

    2.2 session写入随机验证码

    2.3 图片返回前端

    2.4 img对象的src=this.src+='?'

    #html页面
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
       <input type="text" placeholder="验证码" id="code_auth_input">
       <img src="p1/check_code_img" id="code_auth_img">
       <div id="auth_result"></div>
    </body>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        document.getElementById('code_auth_img').onclick=function () {
            console.log(this.src);
            this.src+='?';
        };
        document.getElementById('code_auth_input').onchange=function () {
            console.log(this.value);
               $.ajax(
            {
               url:'{{ request.path_info }}',
               type:'POST',
               data:{'auth_code':this.value},
               success:function (receive_data) {
                   console.log(receive_data);
                   var t_span=document.createElement('span');
                   if (receive_data=='1'){
                       t_span.textContent='验证码认证成功';
                       t_span.style.color='green';
                   }else{
                        t_span.textContent='认证失败';
                        t_span.style.color='red';
                   }
                   $('#auth_result').empty().append(t_span);
               },
            }
        );
        };
    
    </script>
    </html>
    #url的请求过程
    
    path('code_auth_login',views.code_auth_login),
    re_path('check_code_img(w*)',views.check_code_img),#如果path不变化,图片不会刷新
    
    #step 1 view对验证码连接的处理,生成验证吗并保存session
    def check_code_img(request,*args,**kwargs):
        t_img,t_str=create_validate_code()#此函数不展开
        f=BytesIO()
        t_img.save(f,'PNG')#将图片保存到内存中
        request.session['check_code']=t_str
        return HttpResponse(f.getvalue())#将图片已二进制方式返回前端
    
    #step2 view对页面请求的处理
    def code_auth_login(request):
        if request.method=="GET":
            return render(request,'code_auth_login.html')
        if request.method=="POST": #验证码验证
            t_auth_code=request.POST.get('auth_code')
            if t_auth_code==request.session['check_code']:
                  return HttpResponse('1')
            else:
                return HttpResponse('0')

     3、附录 验证码图片生成的代码

    import random
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    
    _letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
    _upper_cases = _letter_cases.upper()  # 大写字母
    _numbers = ''.join(map(str, range(3, 10)))  # 数字
    init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
    
    
    def create_validate_code(size=(120, 30),
                             chars=init_chars,
                             img_type="GIF",
                             mode="RGB",
                             bg_color=(255, 255, 255),
                             fg_color=(0, 0, 255),
                             font_size=18,
                             font_type="Monaco.ttf",
                             length=4,
                             draw_lines=True,
                             n_line=(1, 2),
                             draw_points=True,
                             point_chance=2):
        """
        @todo: 生成验证码图片
        @param size: 图片的大小,格式(宽,高),默认为(120, 30)
        @param chars: 允许的字符集合,格式字符串
        @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
        @param mode: 图片模式,默认为RGB
        @param bg_color: 背景颜色,默认为白色
        @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
        @param font_size: 验证码字体大小
        @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
        @param length: 验证码字符个数
        @param draw_lines: 是否划干扰线
        @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
        @param draw_points: 是否画干扰点
        @param point_chance: 干扰点出现的概率,大小范围[0, 100]
        @return: [0]: PIL Image实例
        @return: [1]: 验证码图片中的字符串
        """
    
        width, height = size  # 宽高
        # 创建图形
        img = Image.new(mode, size, bg_color)
        draw = ImageDraw.Draw(img)  # 创建画笔
    
        def get_chars():
            """生成给定长度的字符串,返回列表格式"""
            return random.sample(chars, length)
    
        def create_lines():
            """绘制干扰线"""
            line_num = random.randint(*n_line)  # 干扰线条数
    
            for i in range(line_num):
                # 起始点
                begin = (random.randint(0, size[0]), random.randint(0, size[1]))
                # 结束点
                end = (random.randint(0, size[0]), random.randint(0, size[1]))
                draw.line([begin, end], fill=(0, 0, 0))
    
        def create_points():
            """绘制干扰点"""
            chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]
    
            for w in range(width):
                for h in range(height):
                    tmp = random.randint(0, 100)
                    if tmp > 100 - chance:
                        draw.point((w, h), fill=(0, 0, 0))
    
        def create_strs():
            """绘制验证码字符"""
            c_chars = get_chars()
            strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开
    
            font = ImageFont.truetype(font_type, font_size)
            font_width, font_height = font.getsize(strs)
    
            draw.text(((width - font_width) / 3, (height - font_height) / 3),
                      strs, font=font, fill=fg_color)
    
            return ''.join(c_chars)
    
        if draw_lines:
            create_lines()
        if draw_points:
            create_points()
        strs = create_strs()
    
        # 图形扭曲参数
        params = [1 - float(random.randint(1, 2)) / 100,
                  0,
                  0,
                  0,
                  1 - float(random.randint(1, 10)) / 100,
                  float(random.randint(1, 2)) / 500,
                  0.001,
                  float(random.randint(1, 2)) / 500
                  ]
        img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲
    
        img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)
    
        return img, strs
    
    
    if __name__=='__main__':
        x,y=create_validate_code()
        print(x,y)

    八、分类搜索

    1、阶段一 创建数据库内容,并在界面全部展示

    1.1 建立表格及关系

    ->文章表

    ->文章主题表(网站给定不能新增)

    ->自定义分类表(用户)

    1.2 添加数据

     

    from django.db import models
    
    # Create your models here.
    class CustomType(models.Model):
        #用户自定义分类,可以动态变化
        typename=models.CharField(max_length=32)
    class ArticleItem(models.Model):
        #网站固定主题,不可变
        itemname=models.CharField(max_length=32)
    
    
    class Article(models.Model):
        #用户的文章,包括标题,内容
        title=models.CharField(max_length=64)
        content=models.CharField(max_length=256)
        custom_type=models.ForeignKey(to='CustomType',to_field='id',on_delete=models.CASCADE)
        article_item=models.ForeignKey(to='ArticleItem',to_field='id',on_delete=models.CASCADE)

     

    1.3 将所有内容返回界面显示

     

    #列出所有文章    
    all_article=models.Article.objects.all()
    #列出所有的主题
    all_item=models.ArticleItem.objects.all()
    #列出所有的分类
    all_type=models.CustomType.objects.all()

    1.4 界面循环显示

    2、阶段二 后台获取过滤后文章列表的方法,并定义获取全部文章的方法

    2.1 后台获取过滤后文章的方法

    定义两个变量,分别代表文章主题的id,和自定义分类的id,为0,则表示all()

    models.xxx.objects.filter(x_id=1,y_id=2)
    #字典作为过滤条件
    models.xxx.objects.filter(**kwargs)

    定义0为all()

        filter_item = {}
        #这里偷懒了,实际应该获取相应的key,可能存在未知bug
        #但是能匹配上正则的,应该此处不会有问题。先这么写吧
        for i,j in kwargs.items():
            if j=='0':
                pass
            else:
                filter_item[i]=j
        #过滤出要显示的文章
        all_article=models.Article.objects.filter(**filter_item)
        #列出所有的主题
        all_item=models.ArticleItem.objects.all()
        #列出所有的分类
        all_type=models.CustomType.objects.all()

    3、url处理及web过滤连接的生成

    3.1 url 正则处理 将kwargs处理为x_id y_id 

    re_path('article_search/(?P<article_item_id>d*)-(?P<custom_type_id>d*).html',views.article_search),
    re_path('article_search(.*)',views.article_search),

    3.2 完成后端

    除了返回前端上面的3种数据外,还需要返回当前的过滤条件。方便前端显示相应的效果,并生成相应的a标签url

     #字典为空的时候 如第一次访问的情况处理
        if not filter_item.get('article_item_id'):
            filter_item['article_item_id']=0
        if not filter_item.get('custom_type_id'):
            filter_item['custom_type_id']=0
    
        #让字典内的key对应的都是int
        #实际if 可以不写
        for i,j in kwargs.items():
            if j=='0':
                filter_item[i]=0
            else:
                filter_item[i]=int(j)

    3.3 构造a标签的url,需要注意全部的0的处理,以及被选定的过滤条件的样式变化

    <div id="search_line">
        <div>
            <span>文章主题</span>
            {% if s_dic.article_item_id == 0 %}
                <a class="be_selected" href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a>
            {% else %}
                <a  href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a>
            {% endif %}
            {% for i in all_item %}
                {% if i.id == s_dic.article_item_id %}
                    <a class="be_selected" href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a>
                {% else %}
                    <a  href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a>
                {% endif %}
            {% endfor %}
        </div>
    <hr>
        <div>
            <span>自定义分类</span>
            {% if s_dic.custom_type_id == 0 %}
                <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a>
            {% else %}
                <a href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a>
            {% endif %}
            {% for i in all_type%}
                {% if i.id == s_dic.custom_type_id %}
                    <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a>
                {% else %}
                    <a href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a>
                {% endif %}
            {% endfor %}
        </div>
    </div>

    4、完整代码

    4.1 html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <style>
        span{
            display: inline-block;
            width: 100px;
            margin: 0 20px;
            text-align: center;
        }
        #search_line  a{
            display: inline-block;
            width: 100px;
            border: 1px solid red;
            margin: 0 20px;
            text-decoration: none;
            text-align: center;
        }
        .be_selected{
            background-color: pink;
            color: palegoldenrod;
        }
    </style>
    <body>
    <h1>分类查询</h1>
    <div id="search_line">
        <div>
            <span>文章主题</span>
            {% if s_dic.article_item_id == 0 %}
                <a class="be_selected" href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a>
            {% else %}
                <a  href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a>
            {% endif %}
            {% for i in all_item %}
                {% if i.id == s_dic.article_item_id %}
                    <a class="be_selected" href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a>
                {% else %}
                    <a  href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a>
                {% endif %}
            {% endfor %}
        </div>
    <hr>
        <div>
            <span>自定义分类</span>
            {% if s_dic.custom_type_id == 0 %}
                <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a>
            {% else %}
                <a href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a>
            {% endif %}
            {% for i in all_type%}
                {% if i.id == s_dic.custom_type_id %}
                    <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a>
                {% else %}
                    <a href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a>
                {% endif %}
            {% endfor %}
        </div>
    </div>
    
    <h1>查询结果</h1>
              {% for i in all_art %}
                  <div>
                      <a href="{{ request.path_info }}" style="text-decoration: none">{{ forloop.counter }}--{{ i.title }}</a>
                  </div>
              {% endfor %}
    </body>
    </html>

    4.2 view函数

    def article_search(request,*args,**kwargs):
        filter_item = {}
        #这里偷懒了,实际应该获取相应的key,可能存在未知bug
        #但是能匹配上正则的,应该此处不会有问题。先这么写吧
        for i,j in kwargs.items():
            if j=='0':
                pass
            else:
                filter_item[i]=j
        #过滤出要显示的文章
        all_article=models.Article.objects.filter(**filter_item)
        #列出所有的主题
        all_item=models.ArticleItem.objects.all()
        #列出所有的分类
        all_type=models.CustomType.objects.all()
    
        #字典为空的时候 如第一次访问的情况处理
        if not filter_item.get('article_item_id'):
            filter_item['article_item_id']=0
        if not filter_item.get('custom_type_id'):
            filter_item['custom_type_id']=0
    
        #让字典内的key对应的都是int
        #实际if 可以不写
        for i,j in kwargs.items():
            if j=='0':
                filter_item[i]=0
            else:
                filter_item[i]=int(j)
    
        return render(request,'article_search.html',{'all_art':all_article,'all_item':all_item,'all_type':all_type,'s_dic':filter_item})

     九、ajax跨域--jsonp

    1、 json与jsonp的区别

    json是一种数据类型 字符串 jsonp是一种方式

    2、requests模块的基本使用 (不做深入了解)

    2.1 response=request.get('https://www.baidu.com')
    2.2 respense.content字节
    2.3 response.encoding='utf-8'
    2.4 response.text 页面内容content字节类型被encoding后
    2.5 response.cookies 查看cookies
    2.6 response.headers  查看headers

    x=requesets.get('http://www.qq.com')
    >>> print(x.headers)
    {'Date': 'Thu, 11 Oct 2018 09:34:34 GMT', 'Content-Type': 'text/html; charset=GB2312', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'squid/3.5.24', 'Vary': 'Accept-Encoding, Accept-Encoding, Accept-Encoding', 'Expires': 'Thu, 11 Oct 2018 09:35:35 GMT', 'Cache-Control': 'max-age=60', 'Content-Encoding': 'gzip', 'X-Cache': 'MISS from tianjin.qq.com'}
    >>> print(x.cookies)
    <RequestsCookieJar[]>
    >>> print(x.encoding)
    GB2312
    >>> print(type(x.text))
    <class 'str'>
    >>> print(type(x.content))
    <class 'bytes'>
    >>> 
    response1 = requests.get("https://fanyi.baidu.com")
    >>> print(response1.cookies)
    <RequestsCookieJar[<Cookie BAIDUID=C07CA5D297A71A59C02A1F223AF0DCDE:FG=1 for .baidu.com/>, <Cookie locale=zh for .baidu.com/>]>
    
    >>> requests.get("http://127.0.0.1:8000/p1/check_code_img").cookies
    <RequestsCookieJar[Cookie(version=0, name='sessionid', value='958utmw44tpj73v5iavf7w6fhpr82xku', port=None, port_specified=False, domain='127.0.0.1',
    domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=1540703854, discard=False, comment=None, comm
    ent_url=None, rest={'HttpOnly': None, 'SameSite': 'Lax'}, rfc2109=False)]>


    2.7 requeset发送带参数的get和post

    #get
    import requests
     
    params = {
        "wd": "python", "pn": 10,
    }
     
    response = requests.get('https://www.baidu.com/s', params=params)
    print(response.url)
    print(response.text)
    -------------------------------
    #POST
    import requests
     
     
    post_data = {'username': 'value1', 'password': 'value2'}
     
    response = requests.post("http://xxx.com/login/", data=post_data)
    ---------------------------------
    #hearders
    
    import requests
    headers = {
     
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/"
                     "537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
    }
     
    response1 =requests.get("https://www.baidu.com", headers=headers)
    response2 =requests.post("https://www.xxxx.com", data={"key": "value"}, 
    headers=headers)
     
    print(response1.headers)
    print(response1.headers['Content-Type'])
    print(response2.text)

    我们要给其他网站发送相应请求信息的的时候,浏览器给我们的server,我们的server转发请求。此时界面可以返回正常的数据。

    3、跨域后浏览器出现的阻塞现象(浏览器的同源策略)

    实际请求流量和返回流量都已经完成,但是浏览器的同源策略阻塞这种跨域流量

    4、jsonp的解决思路

    4.1 基本思路

    4.1.1 使用script的src来get远端数据

    4.1.2 script的srcget到的数据必须是js能读懂的内容。

    4.1.3 远端返回的内容执行callback函数,将具体内容作为参数传到callback函数中。

    4.1.4 html中需要定义callback函数,以及读取内容后的处理过程

    5、 实现方式一: 定义script标签修改src

        function list(x) {
            console.log(x);
        }
        document.getElementById('jsonp_button').onclick=function () {
            var jsonp_obj=document.createElement('script');
            jsonp_obj.src='http://www.jxntv.cn/data/jmd-jxtv2.html';
            document.getElementById('1').appendChild(jsonp_obj);
            document.getElementById('1').removeChild(jsonp_obj);
        };

    注意,此处我们定义的callback函数为list。才专门提供jsonp接口的网址,callback函数的名称会根据你get请求时,callback的值来确定。

    如:https://api.douban.com/v2/book/search?callback=list&q=javascript&count=1

    https://api.douban.com/v2/book/search?callback=niubi&q=javascript&count=1

    6、实现方式二  $.ajax封装生成script标签的过程

    type->POST  /  datatype->jsonp  /  jsonp->callback  /  jsonpcallback->list

        $('#jsonp_ajax_button').click(function () {
            $.ajax(
            {
                url:'https://api.douban.com/v2/book/search?q=javascript&count=1',
                type:'POST',
                dataType:"jsonp",
                jsonp:'callback',
                jsonpCallback: 'list'
            }
            )
        });

    ajax此时会自己生成一个script标签,把src放进去。执行后删除

    7、实现方式三 CORS

    CORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。(较少用,不展开,了解就行)

    8、再提供几个jsonp的接口

    http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301
    http://www.weather.com.cn/data/sk/101110101.html
    >>> import requests
    >>> requests.get('http://www.weather.com.cn/data/sk/101110101.html')
    <Response [200]>
    >>> x=requests.get('http://www.weather.com.cn/data/sk/101110101.html')
    >>> print(x.text)
    {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"æš‚æ— å®žå†µ","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}}
    >>> print(x.encoding)
    ISO-8859-1
    >>> x.encoding='utf-8'
    >>> print(x.text)
    {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}}
    >>>

     十、kindeditor

    1、阶段一 基本使用

    ->下载kindeditor
    ->放入static文件夹下
    ->定义一个textarea 设置id mykindeditor
    ->引入jquery
    ->进入kindeditor中的kindeditor-all.js
    ->KindEditor.create('#mykindeditor',{});
    ->完成 此时界面出现kindeditor框

    <textarea id="content" style=" 100px">
    <script>
    KindEditor.create('#content',{});
    </script>

    2、阶段二 基本参数

    '100%',//可以像素,可以百分比
    height:'300px',//只能像素
    minWidth:'200px',//只能像素
    minHeight:'100px'//只能像素

    <body>
       <textarea id="content" style=" 100px">
       </textarea>
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>
        <script>
            function initKindEditor() {
                var kind = KindEditor.create('#content',{
                    '100%',
                    height:'300px',
                    minWidth:'200px',
                    minHeight:'100px'
                });
            }
            $(function () {
                initKindEditor();
            });
        </script>
    </body>

    3、阶段三 了解一大堆参数

    //1 显示哪些
    item:[] //一个数组,显示哪些工具
    //2 可用哪些
    noDisableItems:[]
    designMode 为false时,要保留的工具栏图标。
    // 3 xss过滤
    filterMode=true
    true时根据 htmlTags 过滤HTML代码,false时允许输入任何代码。
    数据类型: Boolean
    默认值: true
    // 4 设置kindeditor能否被拖动
    resizeType 有3个值 
    2 1 0 2或1或0,2时可以拖动改变宽度和高度,1时只能改变高度,0时不能拖动。
    // 5 禁用鼠标
    useContextmenu
    // 6 syncType
    同步数据的方式,可设置”“、”form”,值为form时提交form时自动同步,空时不会自动同步。
    //7 autoHeightMode 自动增高
    //kindeditor还有很多参数 此处不做展开,以上参数已经能完成大部分功能

    4、kindeditor文件处理

    4.1 上传路径定义 uploadJson

    4.2 文件变量的参数定义(request.files.get(?)) filePostName

     

    4.3 csrf处理  extraFileUploadParams

       <script>
            function initKindEditor() {
                var kind = KindEditor.create('#content',{
                    '100%',
                    height:'300px',
                    minWidth:'200px',
                    minHeight:'100px',
                    uploadJson:'/p1/kind_upload_pic',//定义url
                    extraFileUploadParams:{
                        'csrfmiddlewaretoken':"{{ csrf_token}}"
                    },//csrf处理
                    filePostName:'fafafa'//文件对象定义
                });
            }
            $(function () {
                initKindEditor();
            });//页面框架加载完成运行
        </script>
    ------------------------------------
    def kind_upload_pic(request):
        dic={
            'error':0,
            'url':'/static/image/Desert.jpg',
            'message':None
        }
        if request.method=="POST":
            print(request.FILES.get('fafafa').name)
            return HttpResponse(json.dumps(dic))

    4.4 文件类型如何区分 

    可以通过request.method.get 获取相应的数据

     

    十一、xss过滤

    1、生成界面

    1.1 界面模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>后台管理</title>
        <style>
            .pg-header{
                position: fixed;
                top: 0;
                right: 0;
                left: 0;
                height: 48px;
                background: #2459a2;
            }
            .pg-header .logo{
                float: left;
                color:white;
                line-height: 48px;
            }
            .pg-header .login_info{
                float: right;
                color: white;
                line-height: 48px;
            }
            .pg-header .header-centerctl{
                width: 80%;
                margin: 0 auto;
            }
            .header-centerctl span{
                margin: 2px;
            }
            .left {
                float: left;
            }
            .right{
                float: right;
            }
            .pg-content{
                margin-top: 48px;
    
            }
            .pg-footer{
                height: 20px;
                background: #dddddd;
                text-align: center;
                line-height: 20px;
                position: fixed;
                bottom: 0;
                left: 0;
                right: 0;
            }
            .pg-content .menu{
                position: fixed;
                top:48px;
                bottom: 20px;
                left: 0;
                width: 200px;
                overflow: auto;
            }
            .pg-content .content{
                position: fixed;
                top:48px;
                bottom: 20px;
                right: 0;
                left: 200px;
                background: lightgoldenrodyellow;
                overflow: auto;
    
            }
        .pg-content .menu div{
            height: 50px;
            text-align: center;
            line-height: 50px;
        }
        .pg-content .menu div:hover{
            font-size: 20px;
            background-color: blueviolet;
        }
        </style>
    
    {% block css %}
    {% endblock %}
    
    </head>
    <body style="margin: 0 auto ; 1000px" >
        <div class="pg-header">
            <div class="header-centerctl">
                <div class="logo">简陋的后台管理</div>
                <div class="login_info">
                    <span>头像</span>
                    <span>登录</span>
                    <span>退出</span>
                </div>
            </div>
            <div style="clear: both"></div>
        </div>
        <div class="pg-content">
            <div class="menu left">
                <div style="background: #dddddd">菜单</div>
                <div>list1</div>
                <div>list2</div>
                <div>list3</div>
                <div>list4</div>
                <div>list5</div>
                <div>list6</div>
                <div>list7</div>
                <div>list8</div>
                <div>list9</div>
                <div>list10</div>
                <div>list1</div>
                <div>list2</div>
                <div>list3</div>
                <div>list4</div>
                <div>list5</div>
                <div>list6</div>
                <div>list7</div>
                <div>list8</div>
                <div>list9</div>
                <div>list10</div>
    
    
            </div>
            <div class="content right">
                <div style="min- 1000px">
                      {% block content%}
                      {% endblock %}
                </div>
            </div>
            <div style="clear: both"></div>
        </div>
        <div class="pg-footer">@2018&nbspyomi</div>
    
    {% block js %}
    {% endblock %}
    </body>
    </html>

    1.2 form定义

    class article_form(forms.Form):
        def __init__(self, *args, **kwargs):
            super(article_form, self).__init__(*args, **kwargs)
            self.fields['custom_type_id'].choices=all_custom_type_list
            self.fields['article_item_id'].choices=all_article_item_list
        title=fields.CharField(max_length=64,widget=widgets.TextInput(attrs={'style':" 100%", 'placeholder': '文章标题'}))
        content=fields.CharField(max_length=102400,widget=widgets.Textarea(attrs={'id':"kind_textarea",'style': ' 100%;min-height: 500px','placeholder': '文章正文'}))
        custom_type_id=fields.ChoiceField(
            choices=[],
            widget=widgets.RadioSelect,
        )
        article_item_id=fields.ChoiceField(
            choices=[],
            widget=widgets.RadioSelect,
            )

    1.3 处理浏览器get请求

    def xss_edit_mode(request):
        if request.method=="GET":
           x=article_form({'title':'输入标题','content':'输入正文','custom_type_id':'1','article_item_id':'1'})#定义默认值
           return render(request, 'xss_edit_mode.html', {'x': x})

    1.4 生成完整界面

    {% extends "xss_template.html" %}
    {% block content%}
        <form action="{{ request.path_info }}" method="post">
        {% csrf_token %}
        <div style="margin: 0 auto; 90%">
        <p>标题{{ x.errors.title.0 }}</p>
            {{ x.title }}
        <p>正文{{ x.errors.content.0 }}</p>
            {{ x.content }}
        <p>文章主题{{ x.errors.article_item_id.0 }}</p>
            {{ x.article_item_id }}
        <p>文章分类{{ x.errors.custom_type_id.0 }}</p>
            {{ x.custom_type_id }}
        <input type="submit" name="提交" value="提交">
        </div>
        </form>
    {% endblock %}
    {% block js %}
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>
        <script>
               KindEditor.create('#kind_textarea',{}); //form定义的时候已经定义好了id
        </script>
    {% endblock %}

    2、后端获取form内容

    常规操作,form(request.POST),is_valid(),cleaned_data()

        if request.method=="POST":
             x=article_form(request.POST)
             if x.is_valid():
                 x=x.cleaned_data
                 x['content']=filter_content(x['content'])
                 return render(request, 'xss_edit_show.html', {'x': x})
             else:
                 print('error!!!')
                 return render(request,'xss_edit_mode.html',{'x':x})

    3、内容过滤的基本方法

    3.1 提交过来的内容格式及显示效果

    content的内容实际就是html标签。

    <h1>
        测试一下效果<br />
    </h1>
    <h2>
        <u>哈哈哈</u> 
    </h2>
    <h3>
        啦啦啦
    </h3>
    <p>
        <span>来个图片<span style="font-size:14px;"><img src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif" border="0" alt="" /></span></span> 
    </p>

    3.2 beautifulsoup4对文本标签的处理

    3.2.1 获取被处理后的内容对象 BeautifulSoup(input_content, 'html.parser')

     

    tmp_content_obj = BeautifulSoup(input_content, 'html.parser')

    3.2.2 输出对象内所有的标签(包括嵌套在标签内部的标签)

    print(tmp_content_obj.find_all())
    ------------------------------------------------
    [<h1>测试一下效果<br/></h1>, <br/>, <h2><u>哈哈哈</u></h2>, <u>哈哈哈</u>, <h3>啦啦啦</h3>, 
    <p><span>来个图片<span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span></span></p>,
    <span>来个图片<span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span></span>,
    <span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span>,
    <img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/>]

    循环输出标签名字

    for i in tmp_content_obj.find_all():
           print(i.name)
    ----------------------
    h1
    br
    h2
    u
    h3
    p
    span
    span
    img

    循环输出标签属性

    for i in tmp_content_obj.find_all():
           print(i.name,i.attrs)
    ------------------------
    h1 {}
    br {}
    h2 {}
    u {}
    h3 {}
    p {}
    span {}
    span {'style': 'font-size:14px;'}
    img {'src': 'http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif', 'border': '0', 'alt': ''}

     清除标签内内容的方法,标签依然在,只是内容清空

    for i in tmp_content_obj.find_all():
           i.clear()
           print(i,i.name,i.attrs)
    ----------------------------
    <h1></h1> h1 {}
    <br/> br {}
    <h2></h2> h2 {}
    <u></u> u {}
    <h3></h3> h3 {}
    <p></p> p {}
    <span></span> span {}

    清除内容且清除标签的方法 hidden = True

       for i in tmp_content_obj.find_all():
           i.clear()
           i.hidden = True
           print(i,i.name,i.attrs)
    -------------------------------------------
    h3 {}
     p {}
     span {}
     span {'style': 'font-size:14px;'}
     img {'src': 'http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif', 'border': '0', 'alt': ''}

    清除标签某个属性的方法(删除字典内容的方法)

    del i.attrs[t_attr]

    4、定义白名单

    white_list_dict={
                "font": ['color', 'size', 'face', 'style'],
                'b': [],
                'div': [],
                "span": ['style'],
                "table": [
                    'border', 'cellspacing', 'cellpadding'
                ],
                'th': [
                    'colspan', 'rowspan'
                ],
                'td': [
                    'colspan', 'rowspan'
                ],
                "a": ['href', 'target', 'name'],
                "img": ['src', 'alt', 'title'],
                'p': [
                    'align'
                ],
                "pre": ['class'],
                "hr": ['class'],
                'strong': [],
                'h1': [],
                'h2': []
            }

    5、实例

    #step1 建立白名单
    
    white_list_dict={
                "font": ['color', 'size', 'face', 'style'],
                'b': [],
                'div': [],
                "span": ['style'],
                "table": [
                    'border', 'cellspacing', 'cellpadding'
                ],
                'th': [
                    'colspan', 'rowspan'
                ],
                'td': [
                    'colspan', 'rowspan'
                ],
                "a": ['href', 'target', 'name'],
                "img": ['src', 'alt', 'title'],
                'p': [
                    'align'
                ],
                "pre": ['class'],
                "hr": ['class'],
                'strong': [],
                'h1': [],
                'h2': []
            }
    
    #step2 创建Beautifulsoup类
    from bs4 import BeautifulSoup
    def filter_content(input_content):
       tmp_content_obj = BeautifulSoup(input_content, 'html.parser')
    #step3 寻找某标签,
    #step4 寻找标签可以用find 这个只能找出一个 即使内容中包含很多
    #step5 可以通过clear()hidden() attrs 来删除标签 删除内容或 针对属性做del或修改
    
         #tag=tmp_content_obj.find('script')
         #print(tag,type(tag))
         ##针对取出的bs4.element.Tag对象可以进行clean操作将其清空
         #tag.clear()
         #
         ##将修改后的对象重新转换为文本保存
         ## 返回前端 <script></script>只清空内部内容,不会清空script这个标签
         #tag.hidden = True #会连<script>一起清空
         #
         #tag2=tmp_content_obj.find('h1')
         #print(tag2.attrs)
         #tag2.attrs['style']='color:red'
       print('xxx',tmp_content_obj.find_all())
       for i in tmp_content_obj.find_all():
           print(i.name)
       for i in tmp_content_obj.find_all():
           i.clear()
           i.hidden = True
           print(i,i.name,i.attrs)
       for t_tag in tmp_content_obj.find_all():
           if t_tag.name in white_list_dict:
               tmp_attrs_dict = t_tag.attrs
               tmp_white_attr_list = white_list_dict[t_tag.name]
               for t_attr in list(tmp_attrs_dict):
    if t_attr in tmp_white_attr_list:
                       pass
                   else:
                       del tmp_attrs_dict[t_attr]
           else:
               t_tag.clear()
               t_tag.hidden = True
       return tmp_content_obj.decode()

    6、界面显示

    if x.is_valid():
        x=x.cleaned_data
        x['content']=filter_content(x['content'])
        return render(request, 'xss_edit_show.html', {'x': x})
         {% extends "xss_template.html" %}
    --------------------------------
    {% block content%}
        <form action="{{ request.path_info }}" method="post">
        <div style="margin: 0 auto; 90%">
            {% csrf_token %}
            {{ x.title }}
            <hr>
            {{ x.content | safe }}
    </div>
        </form>
        {{ x.errors }}
    {% endblock %}
    {% block js %}
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>
        <script>
               KindEditor.create('#kind_textarea',{});
        </script>
    {% endblock %}

    最终效果

    十二、单例模式

    1、两种单例模式

    1.1

    class foo1(object):
        instance=None
        def __init__(self):
            self.name='foo1'
        @classmethod
        def get_instance(self):
            if foo1.instance:
                return foo1.instance
            else:
                foo1.instance=foo1()
                return foo1.instance
        def process(self,x):
            print(x)
    
    obj1=foo1.get_instance()
    obj2=foo1.get_instance()
    print(obj1,obj2)
    obj1.process(id(obj1))
    obj2.process(id(obj2))
    <__main__.foo1 object at 0x000000000220EEB8> <__main__.foo1 object at 0x000000000220EEB8>
    35712696
    35712696
    35815496 35815496

    1.2 

    class foo2(object):
        instance=None
        def __new__(cls, *args, **kwargs):
            if foo2.instance:
                return foo2.instance
            else:
                foo2.instance=object.__new__(cls,*args,**kwargs)
                return foo2.instance
        def __init__(self):
            self.name='foo2'
    
    x1=foo2()
    x2=foo2()
    print(id(x1),id(x2))

    2、全局变量和单例模式的区别(了解)

    2.1 全局变量是对一个对象的静态引用,全局变量确实可以提供单例模式实现的全局访问功能,但是它并不能保证应用程序只有一个实例;编码规范也明确的指出应该要少使用全局变量,因为过多的使用全局变量会造成代码难读;全局变量并不能实现继承。

    2.2 单例模式虽然在继承上不能很好的处理,但是还是可以实现继承的;单例模式在类中保存了它的唯实例这个类,可以保证只能创建一个实例,同时它还提供了一个访问该唯一实例的全局访问点。

    附录:大纲

    对比认识model、Form及modelForm
        ->model的功能
            ->创建数据库表
                ->单表 class xxx(models.MODEL)
                    ->创建表
                        ->定义字段
                            ->charField
                            ->EmailField
                            ->IntegerField
                            ->IntegerField(choices=)#此choices要和form中的choices分开
                            ->error_message 与form中的error_message分开
                    ->定制元信息
                      class Meta:
                        ->定制表名                   
                           ->db_table='tb1'#数据库中表名就叫tb1,不再是默认的app+下划线+类名
                        ->联合(唯一)索引
                           ->普通索引 db_index=true 加快查找速度(数据库中会针对每个索引单独创建一个文件)
                           ->index_together 联合索引
                               ->index_together=[('name','sex'),]
                               ->最左前缀
                                   ->select * from where name=xxx 命中
                                    ->select * from where name=xxx and sex=xxx 命中
                                   ->select * from where sex 不能命中
                           ->unique_together 联合唯一索引
                                ->unique_together=[('name','sid'),]
                                ->遵循最左前缀,同时组合只能唯一
                        ->verbose_name='admin名称'
                          verbose_name_pluar='admin名称复数'
                ->多表操作
                    ->ForeignKey
                        ->foreginkey的实质
                            ->表示关系
                            ->约束
                        ->models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users')
                    ->OnetoOne
                        ->OneToONe的本质
                            ->foreginkey的基础
                            ->唯一约束
                    ->ManyToMany
                        ->ManyToMany的本质
                            ->两张表基础上延伸出来第三张表
                            ->两张表双向的foreginkey
                            ->用第三张表来保存双向关系
                            
                    ->关联情况下的删除操作
                        ->两张表
                            ->用户表
                            ->用户类型表
                                ->删除用户类型
                                    ->原始sql会报错
                                    ->django中
                                        models.UserType.objects.filter(id=1).delete()
                                            ->早期django会报错
                                            ->现在django会把相关联的数据全部删除
                                                ->on_delete的参数
                    ->on_delete的参数
                        -> models.CASCADE,删除关联数据,与之关联也删除
                        -> models.DO_NOTHING,删除关联数据,引发错误IntegrityError 数据库抛出异常
                        -> models.PROTECT,删除关联数据,引发错误ProtectedError django抛出异常
                        -> models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
                        -> models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
                        -> models.SET,删除关联数据,
                                      a. 值            与之关联的值设置为指定值,设置:models.SET(值)
                                      b. 函数(返回值)  与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
                    ->一对多的正向、反向查找
                        ->正向
                            ->->双下划线
                                   s4=models.dev_info.objects.filter(did__lt=5)
                                   s5=models.dev_info.objects.filter(did__lt=5).values('dev_ip','dev_port','dev_type__type_name')
                                   s6=models.dev_info.objects.filter(did__lt=6).values_list('dev_ip','dev_port','dev_type__type_name')
                        ->反向
                            ->点与set 格式 b.a_set
                                ->例子:通过dev_type找相应的dev_info
                                -> s7 = models.devtype_table.objects.all()
                                   for i in s7:
                                      print(i.type_name,i.dev_info_set.all())
                                      -----------------------------------------
                                      路由器 <QuerySet [<dev_info: dev_info object (3)>, <dev_info: dev_info object (12)>, <dev_info: dev_info object (21)>, <dev_info: dev_info object (23)>]>
                                      交换机 <QuerySet [<dev_info: dev_info object (5)>, <dev_info: dev_info object (17)>]>
                                      防火墙 <QuerySet [<dev_info: dev_info object (6)>]>
                                      服务器 <QuerySet [<dev_info: dev_info object (4)>, <dev_info: dev_info object (11)>]>
                            ->双下划线 values('a__id')
                                ->此处不要加set
                                -> s8 = models.devtype_table.objects.all().values('type_name','dev_info__dev_ip','dev_info__dev_port')
                                   for i in s8:
                                       print(i)
                                       -----------------------------------------------------------------
                                       {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.1', 'dev_info__dev_port': '44568'}
                                       {'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': '666'}
                                       {'type_name': '路由器', 'dev_info__dev_ip': '11122', 'dev_info__dev_port': '1111'}
                                       {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': '123'}
                                       {'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': '80'}
                                       {'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': '200'}
                                       {'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': '443'}
                                       {'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': '80'}
                                       {'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': '1444'}
                                       
                        ->表格自关联的时候注意的事项
                            related_name 可以将xxx_set重命名为 新的名字 xxx_set==a
                            related_query_name 可以将xxx重命名为新的民资  如b_set==xxx_set               
                                       
                    ->多对多的正向反向操作
                        ->正向
                            a. django创建第三张表
                               m2m.remove
                               m2m.add
                               m2m.set
                               m2m.clear
                               m2m.filter()
                            b. 自定义第三张表(无m2m字段)                
                               自己链表查询
                            c. 自定义第三张表(有m2m字段)
                               m=model.ManyToMany('ClassName',through='TableName',through_fields=['id1','id2'])
                               通过m2m字段查操作 m2m.all()
                               通过m2m字段 clear
                        ->反向
                            ->和foreginkey一样 name_set
                            class UserInfo(models.Model):
                                  username=models.CharField(max_length=32)
                                  email=models.EmailField()
                                  usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE)
                                  u2g=models.ManyToManyField('UserGroup')
                            class UserGroup(models.Model):
                                  groupname=models.CharField(max_length=32)
                            >>> x=models.UserGroup.objects.get(id=3)
                            >>> x.userinfo_set.all()
                                <QuerySet [<UserInfo: UserInfo object (3)>, <UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]>
    
                              
            ->操作数据库表
                ->基本操作
                    ->增加(两种)
                        ->create()
                        ->x=models.xxx(y=y1,z=z2)
                          x.save()
                        ->多对多
                            ->obj.r.add()
                    ->查找  
                        ->all()
                        ->get()
                        ->filter
                        ->exclude
                        
                    ->删除
                        ->delete
                        ->多对多
                            ->obj.r.clear()
                            ->obj.r.remove()
                    ->修改
                        ->update()
                        ->obj.x=xxx
                          obj.save()
                        ->多对多
                            ->obj.r.set([3,5,7])
                ->进阶操作
                    # 获取个数
                    #
                    # models.Tb1.objects.filter(name='seven').count()
                    
                    # 大于,小于
                    #
                    # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
                    # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
                    # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
                    # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
                    # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
                    
                    # in
                    #
                    # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
                    # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
                    
                    # isnull
                    # Entry.objects.filter(pub_date__isnull=True)
                    
                    # contains
                    # 
                    # models.Tb1.objects.filter(name__contains="ven")  相当于like
                    # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
                    # models.Tb1.objects.exclude(name__icontains="ven")
                    
                    # range
                    #
                    # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
                    
                    # 其他类似
                    #
                    # startswith,istartswith, endswith, iendswith,
                    
                    # order by
                    #
                    # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
                    # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc 可以支持多个参数 逐个排序
                    
                    # group by
                    #
                    # from django.db.models import Count, Min, Max, Sum
                    # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
                    # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
                    
                    # limit 、offset
                    #
                    # models.Tb1.objects.all()[10:20]
                    
                    # regex正则匹配,iregex 不区分大小写
                    #
                    # Entry.objects.get(title__regex=r'^(An?|The) +')
                    # Entry.objects.get(title__iregex=r'^(an?|the) +')
                    
                    # date
                    #
                    # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
                    # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
                    
                    # year
                    #
                    # Entry.objects.filter(pub_date__year=2005)
                    # Entry.objects.filter(pub_date__year__gte=2005)
                    
                    # month
                    #
                    # Entry.objects.filter(pub_date__month=12)
                    # Entry.objects.filter(pub_date__month__gte=6)
                    
                    # day
                    #
                    # Entry.objects.filter(pub_date__day=3)
                    # Entry.objects.filter(pub_date__day__gte=3)
                    
                    # week_day
                    #
                    # Entry.objects.filter(pub_date__week_day=2)
                    # Entry.objects.filter(pub_date__week_day__gte=2)
                    
                    # hour
                    #
                    # Event.objects.filter(timestamp__hour=23)
                    # Event.objects.filter(time__hour=5)
                    # Event.objects.filter(timestamp__hour__gte=12)
                    
                    # minute
                    #
                    # Event.objects.filter(timestamp__minute=29)
                    # Event.objects.filter(time__minute=46)
                    # Event.objects.filter(timestamp__minute__gte=29)
                    
                    # second
                    #
                    # Event.objects.filter(timestamp__second=31)
                    # Event.objects.filter(time__second=2)
                    # Event.objects.filter(timestamp__second__gte=31)
            ->queryset所提供的方法
                ->all()
                ->exclude()
                ->order_by()
                ->reverse()倒叙,需要和order_by结合使用
                ->annotate()分组使用
                ->distinct()去重 只能psql用
                ->defer 一次sql不取哪几列,如果写这几列,还会发请求取出
                ->only  一次sql只取哪几列,如果取其他的列,还会发请求取出
                ->extra 了解
                    mysql的函数
                    mysql的组合查询
                ->两个和性能有关的queryset方法
                    ->select_related 与之关联的表所有的都拿过来了
                        ->select_related('foreginkey')只拿这个foreginkey字段
                    ->prefetch_related 分步查询
                        ->users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu')
                            -># select * from users where id > 30
                              # 获取上一步骤中所有的ut_id=[1,2]
                              # select * from user_type where id in [1,2]
                ->时间的处理
                    ->date()
                    ->
                
                .date()
    .datetime()
    def dates(self, field_name, kind, order='ASC'):
        # 根据时间进行某一部分进行去重查找并截取指定内容
        # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
        # order只能是:"ASC"  "DESC"
        # 并获取转换后的时间
            - year : 年-01-01
            - month: 年-月-01
            - day  : 年-月-日
    
        models.DatePlus.objects.dates('ctime','day','DESC')
    
    def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
        # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
        # kind只能是 "year", "month", "day", "hour", "minute", "second"
        # order只能是:"ASC"  "DESC"
        # tzinfo时区对象
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
    
        """
        pip3 install pytz
        import pytz
        pytz.all_timezones
        pytz.timezone(‘Asia/Shanghai’)
        """
    raw原生sql
    models.xxx.objects.raw('select * from from tb') ->会转换为queryset
    如果表结果不对
    models.xxx.objects.raw('select nid as id ,xxname as name from from tb') ->会转换为queryset
        
    def none(self):
        # 空QuerySet对象
    
    get_or_create(username='root',default={'email'='123@123.cn','pwd'=12345})    
    update_or_create()
    exists()*                          
            ->数据验证(弱)
                ->obj.full_clean()方法
                ->full_clean()如果成功,则继续,如果不成功,则程序报错
            ->django预留的save()方法
                clean()方法
                raise 错误
            ->full_clean()每个字段的正则表达式
            ->clean()钩子
            ->报错没有处理
                    
            
        ->form功能
            ->强大的数据验证
        流程->前端发来的数据->form挡一层->放入数据库
        ->基本功能
           ->is_valid()(与models中的full_clean方法对比,full_clean正则表达式,定义clean方法全局二次验证)
           ->cleaned_data
           ->生成html as_json as_p as_table
        ->基本操作
           ->单独建立一个forms.py文件 独立开来便于管理
           ->类中继承forms.Form
           ->name=fields.charField()
              ->生成html 默认input
              ->widget=widget.Textarea(attrs={})定义插件和属性          
              ->request=True  是否必填限制
              ->max_length=32 长度限制
            ->pwd=fields.charField()
              ->widget=widgets.PasswordInput(attr={'class':'c1'})
            ->综上结论
              ->字段用来验证数据
              ->widget插件用来生成html标签
            ->考虑 
               ->我们是否需要用form的所有功能
                    ->验证 (一定用)x=LoginForm(request.POST)
                    ->生成html(我们可以自己定义html,并交给form验证)
                        ->新url的方式提交数据 建议使用form来生成html 这样可以达到保留数据的作用
                        ->如果ajax方式,可以不使用form生成的html,我们可以自己写,但是,同样我们依然能使用form生成
        ->step 2
         select标签的处理
            ->手写
               usertype=fields.choicesField(widget=widgets.SELECT,choices=((0,'超管'),(1,'普通用户'))
            ->  choices=models.usertype.objects.value_list('id','name')
                usertype=fields.choicesField(widget=widgets.SELECT,choices=choices)
            ->刷新
               ->静态字段
               ->字段(实例字段)
               ->实例化变量的过程
                  ->obj.fields ->静态已经固定
                  ->
                    choices=[]
                    def __init__
                      super(UserInfo,self).__init__(*args,**kwargs)
                      #choices
                      self.fields['usertype'].choices=models.usertype.objects.value_list('id','name')
                      #charField
                      self.fields['user_type'].widget.choices=models.usertype.objects.value_list('id','name')
                      两种写法
                   ->modlechoiceField与queryset,empty_lable,to_field_name 需要定制str 不推荐 仅做了解
        ->step 3
           初始化操作 ->传入字典(此处先不考虑获取数据库的情况)
           数据验证
               ->正则表达式验证(django字典)
               ->form无法对数据库中是否存在此字段进行验证
               ->form自带钩子
                   is_valid() -> self.errors() -> self.full_clean()(models中也有full_clean)注意区别->
                       self._clean_fields()
                       self._clean_form()
                       self._post_clean()
                ->_clean_fields 源码
                    ->先过正则表达式验证 
                    ->hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value
                     ->clean_name ->return value
                    ->实例 :验证数据库中是否存在
                             ->错误的raise
                             ->正确的返回值
                ->_clean_form 源码 ->self.clean()方法
                  对整体进行验证
                      ->返回值
                      ->抛出异常 __all__的处理
                      https://www.cnblogs.com/liuzhipenglove/p/8012045.html
                ->post_clean()源码
                ->form自定义正则表达式验证   fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[])
    
                ->执行顺序
                    ->正则表达式
                    ->validators
                    ->clean_%s
                    ->clean_form 
                    ->post_clean
            ->成功信息
                ->obj.cleaned_data
            ->错误信息
                ->Obj.errors[
                '__all__':[] #NONE_CLEANED_FIELDS
                'user':[{'code':xxx,'messages':xxx}],
                'pwd':[{'code':xxx,'messages':xxx}],
                ]
           ->实例
               ->ajax请求 进行登录
                 序列化返回值
                 obj.errors是一个errordict ,要返回ajax 需要对obj.errors 进行序列化处理
                   ->思路一 obj.errors.as_json
                       ajax拿到数据需要json.parse两次
                   ->思路二
                       as_data
                       validators 怎么处理
                       封装了
                       json序列化的过程
                       json.damp(xxx,cls=yyy)
                       自定制damp过程
                       Python 中的isinstance函数,isinstance是Python中的一个内建函数。是用来判断一个对象的变量类型。
                    ->思路三
                       ->有道云
                     
        ->modelForm
             数据库操作+字段验证
                        fields的正则表达式字段
                        ->利用models字段来直接使用
                        
             
             
            ->当model+form的时候 添加(修改)一条数据库内容的操作过程
                ->models 定义表
                ->form 定义对象
                ->form界面生成html
                ->form验证
                ->交给model进行数据库create update的操作
                    代码 有道云
            
            ->modelform的情况
                ->基本定义
                class MYModelForm(forms.ModelForm)
                     class Meta:
                         model=models.UserInfo
                         fields='__all__'
                ->modelform 中文名
                    verbose_name
                ->显示部分字段 
                   fields=['f1','f2']
                   exclude=['f3',f4]#排除谁 
                ->数据验证
                    ->原理
                                         Form->BaseForm-> is_valid 
                    modelform->Base ModelForm->BaseForm-> is_vaild
                    ->方法 is_valid()
                ->Meta中的其他参数字段
                     model
                     fields=None,                     # 字段
                     *error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
                     exclude=None,                    # 排除字段
                     *labels=None,                     # 提示信息
                     help_text                        # 帮助提示
                     *widgets=None                     # 自定义插件 form中的插件,需要注意此处widgets与插件的widgets重名的情况
                     *field_classes=None               # 自定义字段类 (也可以自定义字段) 将邮箱格式定义为url格式
                     localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 默认utc时间
                     ->带*的补充具体代码的字典写法
                ->目前生成html已经完成
                ->数据验证及保存
                   obj.is_valid()  
                   obj.save()
                   ->直接跳过models的操作 保存了数据
                   ->一对多 多对多 直接帮我们跳过了所有的关联操作
                   ->save(false) m2m 的理解
                   ->编辑实例
                       ->直接将model对象放入 instance=model.obj
                       ->新增,或更新 instance参数
                ->modelform的数据验证
                    ->和form的钩子完全一样的
                ->modelform中定义额外字段的方法
                    ->可以额外获取值进行操作
            
            ->回顾
               ->生成html class Meta
               ->mf=xxxmodelform(instance=)
               ->额外标签
               ->各种验证 钩子 is_valid()
               ->保存 save()
                      ->可以拆开 instance.save()
                      ->mf.save_m2m()
                      
            ->自定制djangoadmin时候
              
    ->ajax
        ->背景
            ->xhr
            ->xml httprequest object
            ->ie老版本没有xhr ->jquery 1.x 2.x 3.x
            ->jquery上层提供封装
        ->原生ajax
            ->从socket角度理解
                ->1、建立对象
                   b = new XMLHttpRequest()
                ->2、建立连接 open
                   b.open
                   连接时所需要信息
                   ->method POST、GET、DELETE...
                   ->url   
                   ->async 同步还是异步
                ->3、发送内容 send
                ->4、设置请求头
                    ->set RequestHeader('key','value')
                ->5、获取所有响应头的内容
                    ->getALLResponseHeaders()
                ->6、获取某一个响应头的内容
                    ->getResponseHeader('key')
                ->7、abort()终止请求
            ->从ajax角度理解
                ->$ajax(
                {url
                 method
                 data
                 success 
                }
                )        
                ->new
                ->open 
                ->send
                ->get
            ->基本操作一
                发送请求
                b = new xmlHttpRequest()
                b.open('GET','http://127.0.0.1:8000',true)
                b.send('test=123')
                ->有道云截图
            ->基本操作二
                理解状态码 readState 0-4
                      0-未初始化,尚未调用open()方法;
                      1-启动,调用了open()方法,未调用send()方法;
                      2-发送,已经调用了send()方法,未接收到响应;
                      3-接收,已经接收到部分响应数据;
                      4-完成,已经接收到全部响应数据;
                理解回调函数onreadystatechange=function(){}
                      当readState每次发生变化的时候执行此函数
                    if (b.readState == 4){} 做条件
                理解返回值的类型 
                         responseText 主要用Text 字符串数据 包括json数据
                         responseXML
                知道状态码
                     b.states
                知道还原json数据的方法 JSON.parse(b.responseText)   
                知道如何获取响应头 getResponseHeader 
                知道如何设置请求头 setRequestHeader('key','value') #csrf            
                理解post动作的处理
                    ->设置请求头    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset-UTF-8')
                知道简单的兼容性操作
                    ->y有道代码 
                知道jquery ajax中获取xhr对象的方法 success参数
                        ->$.ajax(
                        {
                        success:function(data,x1,,x2) -
                        }
                        )
        伪ajax 
              理解一:iframe标签的使用
                      iframe发送请求的方法
                       ->代码实例
                       <body>
                           <div>iframe测试</div>
                           <iframe id='my_iframe' src="https://www.baidu.com"></iframe>
                           <div><input id="input_url"></div>
                           <div><input id='iframe_button' type="button" value="提交iframe"></div>
                           <script src="jquery-1.12.4.js"></script>
                           <script>
                           
                              $('#iframe_button').click(function () {
                                  var tmp_url = $('#input_url').val();
                                  $('#my_iframe').attr('src',tmp_url);
                              })
                           </script>
                        </body>
                        ->有道 图片
              理解二:一个form表单 如何利用iframe将表单内容提交
                   form表单 的target参数  target="iframename" iframe放在form表单内 则后台提交
                  
                      <body>
                           <form action="/p1/xml_ajax" method="post" target="ifm1">
                               <iframe name="ifm1" id="ifm1"></iframe>
                               <input type="text" name="name">
                               <input type="text" name="passwd">
                               <input type="submit">
                           </form>
                      </body>
                    ->图片
                     
               理解三 :获取iframe的返回值
                    onload事件的理解 对端返回完成后 触发onload
                    ->document对象
                    ->jquery方式
                         $('#ifm1').contents().find('body').text();    
                      ->实例
                
        多种ajax方法的选择时机:
              普通数据 -> jquery -> xmlHttpRequest -> iframe
    ->3种ajax上传文件的方法
        ->定义一个美观的上传界面的方法 (用一个button来覆盖住input file)
               <div style="position: relative;height: 60px; 100px;" >
                     <input type="file" style="height: 60px; 100px; z-index: 90;position: absolute;top:0;bottom: 0;right: 0;left: 0;opacity: 0">
                     <a style="border-radius: 20px;text-align: center;line-height: 60px;z-index: 50;height: 60px; 100px;background-color: deepskyblue;border: 1px solid darkblue;position: absolute;top:0;bottom: 0;right: 0;left: 0">上传</a>
               </div>
        ->获取<input type='file'>的文件对象
            ->var fobj=document.getElementById('fafafa').files[0]
        ->原生xhr发送文件
            ->依赖FormData对象
                ->不需要添加POST头
                ->fd_obj.append('key',fobj)
                ->fd_obj.append('key2','文本')
                xhrobj.send(fd_obj)
                -->views
                    ->request.Files.get('key')
                    ->request.POST.get('key2')
        ->jquery ajax发送文件
            ->同样依赖FormDatad对象
            $.ajax({
                data:f_obj
            })
            ->添加两个参数
            processData:false, // tell jquery not to process the data
            contentType:false, //tell jquery not to set contentTYPE
            $.ajax({
                data:f_obj
                processData:false, // tell jquery not to process the data
                contentType:false, //tell jquery not to set contentTYPE
            })
        ->formdata在ie中不兼容
        
    ->iframe 发送文件
                    form 中 enctype="multipart/form-data 添加<input type='file'>其他和发普通请求完全一致
                    
                    <body>
                           <form action="/p1/xml_ajax" method="post" target="ifm1"  enctype="multipart/form-data>
                               <iframe name="ifm1" id="ifm1"></iframe>
                               <input type="file" name="fafafa">
                               <input type="submit">
                           </form>
                     </body>
    归纳
        文件传输
            ->iframe >jquery > 原生ajax
    
    ->图片预览
        ->图片onload 
        ->iframe submit
        ->返回值 $('iframename').contents().find('body').text()
        ->获取图片path
        ->在相应位置插入图片
            var img_obj=document.createElement('img');
            img_obj.src=tmp_path;
            //img_obj.style.width='200px';
            //img_obj.style.height='400px';
            $('#pre_view').empty().append(img_obj);
        ->有道云 代码 图片
        
    ->验证码
        ->分析
            ->不同的人 不同的验证码
            ->和session关联
            ->生成新的验证码 session更改
        ->流程 
            ->访问界面
                ->创建图片并返回用户
                ->session存放验证码
            ->用户post登录
                ->验证post的验证码和session的验证码是否一致
            ->图片的url如何处理 
               预备
                ->图片路径
                ->界面处理
                ->点击刷新 ->访问一个url
                ->httpresonse返回byte数据,img标签显示图片
              实现
                 pillow模块生成验证码图片句柄f和字符串
                 生成过程需要依赖一个字体文件 ttf
                 f.save()保存图片
                 open 文件 httpresponse rb 给前方
                 如果文件一直被覆盖
                 如何写入内存BIOSIO 跳过文件保存
                 字符串写入session中
                 session和提交的验证码对比
                 点击刷新(如果url不变浏览器不重新发送请求)
            ->代码实例
            
                              
    ->kindeditor
       阶段一
        ->下载kindeditor
        ->放入static文件夹下
        ->定义一个textarea 设置id mykindeditor
        ->引入jquery
        ->进入kindeditor中的kindeditor-all.js
        ->KindEditor.create('#mykindeditor',{});
        ->完成 此时界面出现kindeditor框
       阶段二
         字典定义kindeditor 基本参数
         '100%',//可以像素,可以百分比
         height:'300px',//只能像素
         minWidth:'200px',//只能像素
         minHeight:'100px'//只能像素
      阶段三
         一大堆参数
         part 1 显示哪些
         item:[] //一个数组,显示哪些工具
         part 2 可用哪些
         noDisableItems:[]
         designMode 为false时,要保留的工具栏图标。
         part 3 xss过滤
         filterMode
               true时根据 htmlTags 过滤HTML代码,false时允许输入任何代码。
               数据类型: Boolean
               默认值: true
               ->有道云默认值
               ->只是相对 我们在后端还要定义过滤规则
        part 4 
        resizeType 设置kindeditor能否被拖动
        3个值 2 1 0 2或1或0,2时可以拖动改变宽度和高度,1时只能改变高度,0时不能拖动。
        part 5 禁用鼠标
        useContextmenu
        part 6 syncType
          同步数据的方式,可设置”“、”form”,值为form时提交form时自动同步,空时不会自动同步。
        part 7 一些参数 
           ->有道云 复制
        part 8 uploadJson 重要 指定上传文件的url post动作
         uploadJson:'/p1/kind_upload_pic',上传文件
        part 9 上传文件如何处理
           ->有道云看代码  
           ->上传文件的类型如何判断 ?/dir=xxx  get动作
           
        part 10 autoHeightMode 自动增高
        part 11 csrf处理
        part 12 定义发送文件的name
                filePostName
                
    ->分类搜索
        step 1 
            创建数据库内容
                 ->文章表
                 ->文章主题表(网站给定不能新增)
                 ->自定义分类表(用户)
            界面列出所有内容的标题
                 models.xxx.objects.all()
        step 2 
            后台获取过滤后文章的方法
            models.xxx.objects.filter(x_id=1,y_id=2)
            字典作为过滤条件
            models.xxx.objects.filter(**kwargs)
            定义0为all()
        step 3 
             url 正则处理 将kwargs处理为x_id y_id    
             完成后端
             构造a标签的url
             选定时候的样式添加
             全部的处理
    ->json与jsonp
        json是一种数据类型 字符串
        jsonp是一种方式
        
        request模块
        request发送get
           response=request.get('https://www.baidu.com')
           respense.content字节
           response.encoding='utf-8'
           response.text
           response.cookies
           response.header
        requeset发送post
        
        此时浏览器没有给外部发送请求  浏览器给我们的server,我们的server转发请求
        通过js发送请求 ->ajax发送请求。
    x=requesets.get('http://www.qq.com')
    >>> print(x.headers)
    {'Date': 'Thu, 11 Oct 2018 09:34:34 GMT', 'Content-Type': 'text/html; charset=GB2312', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'squid/3.5.24', 'Vary': 'Accept-Encoding, Accept-Encoding, Accept-Encoding', 'Expires': 'Thu, 11 Oct 2018 09:35:35 GMT', 'Cache-Control': 'max-age=60', 'Content-Encoding': 'gzip', 'X-Cache': 'MISS from tianjin.qq.com'}
    >>> print(x.cookies)
    <RequestsCookieJar[]>
    >>> print(x.encoding)
    GB2312
    >>> print(type(x.text))
    <class 'str'>
    >>> print(type(x.content))
    <class 'bytes'>
    >>> 
               ---------------------
    
        使用xhr对象发送get请求 代码及现象
        ->浏览器具有同源策略 
        ->cdn
        ->创建script标签 添加src 获取内容
        ->返回值为ok的情况
        ->返回值为alert()的情况
        ->返回的数据必须是js格式
        ->对端提供功能 返回函数式内容
        ->定义js函数
        ->服务器接收参数 返回服务端定义的操作 动态函数
        ->执行完成后,删除插入的script
    jsonp就是一种方式
    callback
    jsonp只能发送get请求 不能发送get请求
    
    通过js写
    
    通过jquery写jsonp的请求的实现过程及原理
    
    CORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。
       
    http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301
    
    http://www.weather.com.cn/data/sk/101110101.html
    >>> import requests
    >>> requests.get('http://www.weather.com.cn/data/sk/101110101.html')
    <Response [200]>
    >>> x=requests.get('http://www.weather.com.cn/data/sk/101110101.html')
    >>> print(x.text)
    {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}}
    >>> print(x.encoding)
    ISO-8859-1
    >>> x.encoding='utf-8'
    >>> print(x.text)
    {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}}
    >>> 
    
    
    
                
  • 相关阅读:
    点击cell后 cell的背景不变,cell上的字体颜色发生改变的功能实现
    各种属性设置
    多列表 ,菜单
    正则表达式
    多个storyboard之间的跳转问题
    关于uicollectionview的个人学习
    uiscrollview的自动布局
    手动自动布局
    关于简单的跳转问题
    深入理解@class和#import的区别
  • 原文地址:https://www.cnblogs.com/yomi/p/9769327.html
Copyright © 2011-2022 走看看