一、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"}}
>>>
python笔记-20 django进阶 (model与form、modelform对比,三种ajax方式的对比,随机验证码,kindeditor)的更多相关文章
-
form表单4种提交方式
<!DOCTYPE html><html> <head> <title>JavaScript表单提交四种方式</title> <met ...
-
Django框架(十)--ORM多对多关联关系三种创建方式、form组件
多对多的三种创建方式 1.全自动(就是平常我们创建表多对多关系的方式) class Book(models.Model): title = models.CharField(max_length=32 ...
-
《ASP.NET MVC4 WEB编程》学习笔记------Entity Framework的Database First、Model First和Code Only三种开发模式
作者:张博出处:http://yilin.cnblogs.com Entity Framework支持Database First.Model First和Code Only三种开发模式,各模式的开发 ...
-
2019年6月14日 Web框架之Django_07 进阶操作(MTV与MVC、多对多表三种创建方式、前后端传输数据编码格式contentType、ajax、自定义分页器)
摘要 MTV与MVC 多对多表三种创建方式 ajax ,前后端传输数据编码格式contentType 批量插入数据和自定义分页器 一.MVC与MTV MVC(Model View Controller ...
-
django----多对多三种创建方式 form组件
目录 多对多三种创建方式 全自动 全手动 半自动 form组件 基本使用 form_obj 及 is_valid() 前端渲染方式 取消前端自动校验 正则校验 钩子函数(Hook方法) cleaned ...
-
python selenium 三种等待方式详解[转]
python selenium 三种等待方式详解 引言: 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面加载太慢造成了元素不可见,那就必须要加等待 ...
-
Django多对多表的三种创建方式,MTV与MVC概念
MTV与MVC MTV模型(django): M:模型层(models.py) T:templates V:views MVC模型: M:模型层(models.py) V:视图层(views.py) ...
-
django 模板语法和三种返回方式
模板 for循环 {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} if语句 ...
-
jquery.validate+jquery.form提交的三种方式
原文:http://www.cnblogs.com/datoubaba/archive/2012/06/06/2538873.html jquery.validate+jquery.form提交的三种 ...
随机推荐
-
Abp集成Swagger的最佳实践
1.在项目中添加nuget包 Abp.Web.Api.SwaggerTool 2.在项目Abp模块的DependsOn添加AbpWebApiSwaggerToolModule Run It,启动项目, ...
-
TRUNCATE,DORP,DELETE
TRUNCATE,DORP,DELETE 相同点: truncate和不带where子句的delete, 以及drop都会删除表内的数据 不同点: 1. truncate和 delete只删除数据不删 ...
-
转载RabbitMQ入门(3)--发布和订阅
发布和订阅 (使用java 客户端) 在先前的指南中,我们创建了一个工作队列.这工作队列后面的假想是每一个任务都被准确的传递给工作者.在这部分我们将会做一些完全不同的事情–我们将一个消息传递给多个消费 ...
-
Linux下服务器重启
Linux关闭和重启系统一般使用相同的命令可以实现. 在Linux系统下常用在关机/重启命令有shutdown.halt.reboot和init,但每个命令的内部工作过程是不同的. 1.shutdow ...
-
Docker(十二):Docker集群管理之Compose
1.Compose安装 curl -L https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname ...
-
金融量化分析【day111】:Matplotib-图标标注
一.图像标注 1.股票 df = pd.read_csv('601318.csv') df.plot() plt.plot([1,3,4,5]) plt.plot([5,8,7,9]) plt.tit ...
-
《Linux就该这么学》第九天课程
这次课程主要学了如何分区以及RAID技术 有扩展分区:扩展分区可以指定sdb*中的*(1~4)为多少,则其他三个为主分区,扩展分区中的逻辑分区sdb*(*从5开始)无扩展分区:四个皆为主分区 RAID ...
-
【消息】Pivotal Pivots 开源大数据处理的核心组件
Pivotal Pivots 开源大数据处理的核心组件 Pivotal 今天宣布将其大数据套件的三个核心组件开源,同时商业版本继续提供更高级特性和商业支持服务. 这三个开源的组件分别是: GemFir ...
-
【设计模式】—— 桥接模式Bridge
前言:[模式总览]——————————by xingoo 模式意图 这个模式使用的并不多,但是思想确实很普遍.就是要分离抽象部分与实现部分. 实现弱关联,即在运行时才产生依赖关系. 降低代码之间的耦合 ...
-
Cobbler图文详解安装及遇到的问题说明
一.介绍 Cobbler是一个使用Python开发的开源项目,通过将部署系统所涉及的所有服务集中在一起,来提供一个全自动批量快速建立linux系统的网络环境, Cobbler提供了DHCP管理,YUM ...