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:\Python36\lib\site-packages\django\db\models\fields\related_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;width: 100px;" >
     <input type="file" id='fafafa' style="height: 60px;width: 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;width: 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="width: 100px">
<script>
KindEditor.create('#content',{});
</script>

2、阶段二 基本参数

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

<body>
   <textarea id="content" style="width: 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',{
                width:'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',{
                width:'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 ;width: 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-width: 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':"width: 100%", 'placeholder': '文章标题'}))
    content=fields.CharField(max_length=102400,widget=widgets.Textarea(attrs={'id':"kind_textarea",'style': 'width: 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;width: 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;width: 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;width: 100px;" >
                 <input type="file" style="height: 60px;width: 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;width: 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 基本参数
     width:'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"}}
>>> 



            

 

posted @ 2018-10-14 22:29  yomi_note  阅读(1287)  评论(0编辑  收藏  举报