一、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='') # 将指定条件的数据更新,均支持 **kwargs
obj = models.Tb1.objects.get(id=1)
obj.c1 = ''
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': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '', 'dev_info__dev_port': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': ''}
{'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': ''}
{'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': ''}
{'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': ''}
{'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': ''}
{'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': ''}
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=='':
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=='':
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=='':
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=='':
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 yomi</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':'','article_item_id':''})#定义默认值
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="" 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="" 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="" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span>,
<img alt="" border="" 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': '', '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': '', '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': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '', 'dev_info__dev_port': ''}
{'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': ''}
{'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': ''}
{'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': ''}
{'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': ''}
{'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': ''}
{'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': ''} ->表格自关联的时候注意的事项
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":"","temp":"23.3","WD":"西åé£","WS":"å°äº3级","SD":"52%","AP":"962.7hPa","njd":"ææ å®åµ","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"","Radar":"JC_RADAR_AZ9290_JB"}}
>>> print(x.encoding)
ISO-8859-1
>>> x.encoding='utf-8'
>>> print(x.text)
{"weatherinfo":{"city":"西安","cityid":"","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"","Radar":"JC_RADAR_AZ9290_JB"}}
>>>