python笔记-20 django进阶 (model与form、modelform对比,三种ajax方式的对比,随机验证码,kindeditor)
一、model深入
1、model的功能
1.1 创建数据库表
1.2 操作数据库表
1.3 数据库的增删改查操作
2、创建数据库表的单表操作
2.1 定义表对象
class xxx(models.MODEL)
2.2 定义字段
CharField EmailField TextField IntegerField AutoField BooleanField DateField DateTimeField GenericIPAddressField IntegerField(choices=)#此choices可以搭配djangoAdmin使用,要和form中的choices分开
2.3 定义字段参数
null 数据库中字段是否可以为空 db_column 数据库中字段的列名 default 数据库中字段的默认值 primary_key 数据库中字段是否为主键 db_index 数据库中字段是否可以建立索引 unique 数据库中字段是否可以建立唯一索引 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) verbose_name Admin中显示的字段名称 blank Admin中是否允许用户输入为空 editable Admin中是否可以编辑 help_text Admin中该字段的提示信息 error_messages #与form中的error_message分开 models的验证功能较弱,故基本用不上 validators #自定义认证规则 models的验证功能较弱,故基本用不上
2.4 定制元信息 class Meta
2.4.1 定制表名
db_table='tb1' #数据库中表名就叫tb1,不再是默认的app+下划线+类名
2.4.2 联合(唯一)索引
普通索引 db_index (普通索引非元信息,摆在此处做对比)
唯一索引 unique (唯一索引非元信息,摆在此处做对比)
联合索引 db_together
联合唯一索引 unique_together
#普通索引 db_index=true #加快查找速度(数据库中会针对每个索引单独创建一个文件),一列数据一个文件 #联合索引 index_together=[('name','sex'),] #将多列数据生成一个索引文件 #联合唯一索引 unique_together=[('name','sid'),] #与联合索引相同,同时加上唯一性限制,组合只能唯一
2.4.3 最左前缀
->select * from where name=xxx 命中 ->select * from where name=xxx and sex=xxx 命中 ->select * from where sex 不能命中
2.4.4 djangoadmin及modelform中的显示名(元信息)
verbose_name='admin名称' verbose_name_pluar='admin名称复数'
3、创建数据库表的多表操作
3.1 ForeignKey 一对多
3.1.1 foreginkey的实质
->表示关系
->约束
models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users',on_delete=models.CASC)
3.2 OnetoOne 一对一
3.2.1 OneToONe的本质
->foreginkey的基础
->唯一约束
3.3 ManyToMany多对多
3.3.1 ManyToMany的本质
->两张表基础上延伸出来第三张表
->两张表双向的foreginkey
->用第三张表来保存双向关系
u2g=models.ManyToManyField('UserGroup')
3.4 多表字段的参数
3.4.1 基本参数
to #关联的表 to_field #关联的字段 related_name #可以将xxx_set重命名为 新的名字 xxx_set==a related_query_name #可以将xxx重命名为新的民资 如b_set==xxx_set
3.4.2 on_delete的参数
在一对多关联情况下我们进行删除操作,假如有两张表,一张用户表,一张用户类型表, 我们在删除用户类型表中的数据的时候,由于关联关系的存在,原始sql会报错。
在django中 models.UserType.objects.filter(id=1).delete()
早期django会报错
现在django,我们可以定义on_delete()参数来设定在删除关联表的时候,django对关联数据进行的操作。
models.CASCADE, #删除关联数据,与之关联也删除 models.DO_NOTHING, #删除关联数据,引发错误IntegrityError 数据库抛出异常 models.PROTECT, #删除关联数据,引发错误ProtectedError django抛出异常 models.SET_NULL, #删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) models.SET_DEFAULT, #删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) models.SET, #删除关联数据, #a. 值 与之关联的值设置为指定值,设置:models.SET(值) #b. 函数(返回值) 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
4、数据库基本操作
4.1 增加(两种)
#method 1 models.Tb1.objects.create(c1='xx', c2='oo') #增加一条数据,可以接受字典类型数据 **kwargs #method 2 obj = models.Tb1(c1='xx', c2='oo') obj.save()
4.2 删除
models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据
4.3 修改
models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs obj = models.Tb1.objects.get(id=1) obj.c1 = '111' obj.save() # 修改单条数据
4.4 查找
models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) models.Tb1.objects.all() # 获取全部 models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 models.Tb1.objects.exclude(name='seven') # 排除指定条件的数据
5、数据库连表操作的基本操作
5.1 一对多的正向操作
5.1.1 通过.(点)来获取对象
5.1.2 通过双下划线来获取对象
s4=models.dev_info.objects.filter(did__lt=5) s5=models.dev_info.objects.filter(did__lt=5).values('dev_ip','dev_port','dev_type__type_name') s6=models.dev_info.objects.filter(did__lt=6).values_list('dev_ip','dev_port','dev_type__type_name')
5.2 一对多的反向操作
(注意,自关联时候,related_name、related_query_name 参数)
5.2.1 通过'.+classname+_+set'来操作
s7 = models.devtype_table.objects.all() for i in s7: print(i.type_name,i.dev_info_set.all()) ----------------------------------------- 路由器 <QuerySet [<dev_info: dev_info object (3)>, <dev_info: dev_info object (12)>, <dev_info: dev_info object (21)>, <dev_info: dev_info object (23)>]> 交换机 <QuerySet [<dev_info: dev_info object (5)>, <dev_info: dev_info object (17)>]> 防火墙 <QuerySet [<dev_info: dev_info object (6)>]> 服务器 <QuerySet [<dev_info: dev_info object (4)>, <dev_info: dev_info object (11)>]>
5.2.2 通过双下划线来操作,双下划线时候不需要set
s8=models.devtype_table.objects.all().values('type_name','dev_info__dev_ip','dev_info__dev_port') for i in s8: print(i) ----------------------------------------------------------------- {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.1', 'dev_info__dev_port': '44568'} {'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': '666'} {'type_name': '路由器', 'dev_info__dev_ip': '11122', 'dev_info__dev_port': '1111'} {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': '123'} {'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': '80'} {'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': '200'} {'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': '443'} {'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': '80'} {'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': '1444'}
5.3 多对多的正向操作
5.3.1 通过.(点)来获取对象
all/filter/remove/clear/set/ 可以操作id,可以操作对象
#all >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup obj ect (3)>]> #filter >>> x.u2g.filter() <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup obj ect (3)>]> #delete不生效 >>> x.u2g.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'ManyRelatedManager' object has no attribute 'delete' #clear >>> x.u2g.clear() >>> x.u2g.all() <QuerySet []> #set >>> x.u2g.set([1,2,3]) >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]> #remove >>> x.u2g.remove(1) >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]> #add 可以添加对象 >>> y=models.UserGroup.objects.get(id=1) <UserGroup: UserGroup object (1)> >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]> >>> x.u2g.add(y) >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (1)>, <UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]> #remove 可以删除对象 >>> x.u2g.remove(y) >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (2)>, <UserGroup: UserGroup object (3)>]> #set可以设置对象,但是需要添加进列表 >>> x.u2g.set(y) Traceback (most recent call last): File "<console>", line 1, in <module> File "C:\Python36\lib\site-packages\django\db\models\fields\related_d escriptors.py", line 975, in set objs = tuple(objs) TypeError: 'UserGroup' object is not iterable >>> z=[y,] >>> x.u2g.set(z) >>> x.u2g.all() <QuerySet [<UserGroup: UserGroup object (1)>]>
5.3.2 通过双下划线来获取对象
和foreginkey一样,此处不展开,需要注意values_list,values操作是对queryset进行的操做
5.4 多对多的反向操作
5.4.1 通过.点set操作
x=models.UserGroup.objects.get(id=3) >>> x.userinfo_set.all() <QuerySet [<UserInfo: UserInfo object (3)>, <UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]>
5.4.2 通过双下划线操作
>>> i=models.UserGroup.objects.filter(id=1) >>> i <QuerySet [<UserGroup: UserGroup object (1)>]> >>> i.values_list() <QuerySet [(1, 'CEO')]> >>> i.values_list('id') <QuerySet [(1,)]> >>> i.values_list('id','groupname') <QuerySet [(1, 'CEO')]> >>> i.values_list('id','groupname','userinfo__id') <QuerySet [(1, 'CEO', 3), (1, 'CEO', 1)]> >>> i.values_list('id','groupname','userinfo__name') <QuerySet [(1, 'CEO', 'xxx1'), (1, 'CEO', 'liuliuliu')]>
6、queryset对象的常用操作
#取值 all() exclude() filter() #排序 order_by() reverse()倒叙,需要和order_by结合使用 #删除 delete() #分组、去重 annotate()分组使用 distinct()去重 只能psql用 #获取对象的部分信息 类似values。values_list 但是取出的是queryset defer 一次sql不取哪几列,如果写这几列,还会发请求取出 only 一次sql只取哪几列,如果取其他的列,还会发请求取出
7、queryset中和性能相关的两个重要参数
7.1 select_related
select_related 方法在查询的时候把与之关联的表所有的都拿过来了
我们可以指定只拿相关联的表的某个字段
select_related('foreginkey')
只拿这个foreginkey字段
models.xxx.objects.all().select_related()
7.2 prefetch_related 分步查询
其流程是先查单张表,然后查出有多表的字段的取值范围,再去关联表中取出在这个取值范围内的数据
users=models.User.objects.filter(id__gt=30).prefetch_related('ut','tu') #step1 select * from users where id > 30 #step2 获取上一步骤中所有的ut_id=[1,2] # select * from user_type where id in [1,2]
8、数据库的补充操作
8.1 运行原生sql语句的raw方法
#raw原生sql models.xxx.objects.raw('select * from from tb') #会转换为queryset 如果表结果不对 models.xxx.objects.raw('select nid as id ,xxname as name from from tb') #会转换为queryset get_or_create(username='root',default={'email'='123@123.cn','pwd'=12345}) update_or_create() exists()
8.2 其他的一些补充
# 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于 # # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull # Entry.objects.filter(pub_date__isnull=True) # contains # # models.Tb1.objects.filter(name__contains="ven") 相当于like # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似 # # startswith,istartswith, endswith, iendswith, # order by # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc 可以支持多个参数 逐个排序 # group by # # from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset # # models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写 # # Entry.objects.get(title__regex=r'^(An?|The) +') # Entry.objects.get(title__iregex=r'^(an?|the) +') # date # # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year # # Entry.objects.filter(pub_date__year=2005) # Entry.objects.filter(pub_date__year__gte=2005) # month # # Entry.objects.filter(pub_date__month=12) # Entry.objects.filter(pub_date__month__gte=6) # day # # Entry.objects.filter(pub_date__day=3) # Entry.objects.filter(pub_date__day__gte=3) # week_day # # Entry.objects.filter(pub_date__week_day=2) # Entry.objects.filter(pub_date__week_day__gte=2) # hour # # Event.objects.filter(timestamp__hour=23) # Event.objects.filter(time__hour=5) # Event.objects.filter(timestamp__hour__gte=12) # minute # # Event.objects.filter(timestamp__minute=29) # Event.objects.filter(time__minute=46) # Event.objects.filter(timestamp__minute__gte=29) # second # # Event.objects.filter(timestamp__second=31) # Event.objects.filter(time__second=2) # Event.objects.filter(timestamp__second__gte=31) # extra # # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) # Entry.objects.extra(where=['headline=%s'], params=['Lennon']) # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) # F # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q # # 方式一: # Q(nid__gt=10) # Q(nid=8) | Q(nid__gt=10) # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') # 方式二: # con = Q() # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # 执行原生SQL # # from django.db import connection, connections # cursor = connection.cursor() # cursor = connections['default'].cursor() # cursor.execute("""SELECT * from auth_user where id = %s""", [1]) # row = cursor.fetchone()
9、数据验证(弱)
models主要是用来做数据库的相关操作,对于数据验证,功能比较薄弱,建议和form搭配,使用form的数据验证功能
9.1 models的数据验证有full_clean()方法
full_clean()方法有两个部分,
第一个部分是验证字段的正则表达式,是否匹配
第二部分是给了一个self.clean()的钩子
9.2 self.clean()钩子函数
我们可以通过self.clean()钩子对数据进行验证。
验证成功则可以执行obj.save()方法,否则程序报错。
models的验证功能建议用form实现
二、form深入
1、form的功能
form强大的数据验证功能
2、form处理的流程
前端发来的数据->form挡一层->放入数据库
3、form基本功能(验证+标签生成)
3.1 is_valid()验证(与models中的full_clean方法对比,full_clean正则表达式,定义clean方法全局二次验证)
3.2 cleaned_data获取验证后的数据
3.3 生成html as_json as_p as_table
4、form的操作--阶段一
4.1 单独建立一个forms.py文件 独立开来便于管理
4.2 类中继承forms.Form
4.3 name=fields.charField()定义字段并设置参数
4.3.1 生成html 默认input
4.3.2 widget=widget.Textarea(attrs={})定义插件和属性
4.3.3 request=True 是否必填限制
4.3.4 max_length=32 长度限制
4.3.5 pwd=fields.charField()
4.3.6widget=widgets.PasswordInput(attr={'class':'c1'})
4.4 综上结论
4.4.1 字段用来验证数据
4.4.2 widget插件用来生成html标签
4.5 考虑
我们是否需要用form的所有功能?
4.5.1 验证 (一定用)x=LoginForm(request.POST)
4.5.2 生成html(我们可以自己定义html,并交给form验证)
假如:
->新url的方式提交数据 建议使用form来生成html 这样可以达到保留数据的作用 -> ajax方式提交数据,可以不使用form生成的html,我们可以自己写,但是,同样我们依然能使用form生成
5、form操作--阶段二 (select标签的选项的处理)
上一篇博客已经写到了form中widget的各种选项插件了,此处不做插件说明。
5.1 手写选项
usertype=fields.choicesField(widget=widgets.SELECT,choices=((0,'超管'),(1,'普通用户'))
5.2 数据库中获取
choices=models.usertype.objects.value_list('id','name') usertype=fields.choicesField(widget=widgets.SELECT,choices=choices)
5.3 新增字段后需要重启服务的解决方法(2种)
5.3.1 __init__()方法*(重点方法)
class user3(forms.Form): def __init__(self,*args,**kwargs): super(user3, self).__init__(*args,**kwargs) self.fields['user_type'].widget.choices=models.user_type.objects.values_list('id','type') name=fields.CharField(max_length=32) user_type=fields.CharField(widget=widgets.Select(choices=[],) )
5.3.2 使用django 自带的modlechoiceField(只做了解)
6、form操作--阶段三
6.1 初始化操作 ->传入字典
6.2 数据验证及钩子过程
6.2.1 正则表达式验证(django字典)
6.2.2 form无法对数据库中是否存在此字段进行验证所以需要使用form自带钩子
6.2.3 源码实现流程
is_valid() -> self.errors() -> self.full_clean()(models中也有full_clean)
6.2.4 源码的几个钩子
注意区别->
self._clean_fields()
self._clean_form()
self._post_clean()
6.3 _clean_fields 源码分析
6.3.1 先过正则表达式验证
6.3.2 hasattr(self, 'clean_%s' % name)
6.3.3 value = getattr(self, 'clean_%s' % name)()
6.3.4 return value
6.3.5 self.cleaned_data[name] = value
6.3.6 返回正确的值
6.3.7 raise错误的方法
def clean_email(self): raise ValidationError('测试raise错误',code=250)
6.4 _clean_form 源码分析
6.4.1 self.clean()
def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """ return self.cleaned_data
6.4.2 对整体进行验证抛出异常 __all__的处理 ('__all__':[] #NONE_CLEANED_FIELDS)
->form自定义正则表达式验证 fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[])
6.5 _post_clean源码
6.6 form自定义正则表达式验证的方法
form自定义正则表达式验证 fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[])
6.7 form验证的完整执行顺序
6.7.1 正则表达式
6.7.2 validators
6.7.3 clean_%s
6.7.2 clean
6.7.5 post_clean
6.7.6 成功信息 obj.cleaned_data
6.7.7 错误信息
Obj.errors[ '__all__':[] #NONE_CLEANED_FIELDS 'user':[{'code':xxx,'messages':xxx}], 'pwd':[{'code':xxx,'messages':xxx}], ]
7、实例 ajax请求 进行登录的返回值处理
需要考虑一,验证失败,objects.errors是一个ValidationError,如何处理
需要考虑的情况二,验证成功,要去数据库中取值,queryset对象如何序列号,queryset对象内的时间格式如何序列化
7.1 情况一,验证失败,ValidationError的处理方式(2种方式)
obj.errors是一个errordict ,要返回ajax 需要对obj.errors 进行序列化处理
7.1.1 思路一 obj.errors.as_json 双方进行两次json
<script> $(function () { $('#button').click( function () { $.ajax({ url:'{{ request.path_info }}', type:'POST', data:$('#auth_form').serialize(), success:function (r_data) { dict=JSON.parse(r_data); console.log(dict); err_dict=JSON.parse(dict['error']); console.log(err_dict); }, error:function () { pass } }) } ) }) </script> def login(request): ret={'status':True,'error':None,'data':None} if request.method=="GET": return render(request,'l4/login.html') if request.method=="POST": obj=login_form(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) ret['error']= obj.errors.as_json() #第一次json return HttpResponse(json.dumps(ret)) #第二次json
7.1.2 思路二 obj.errors.as_data {'key':validators}
原理:json.dumps()时我们可以加入参数cls,json在dumps的过程中会执行cls的default方法
class newdumps(json.JSONEncoder): def default(self, fields): if isinstance(fields,ValidationError): return {'code':fields.code,'messages':fields.messages} else: return json.JSONEncoder.default(self,fields) def login(request): ret={'status':True,'error':None,'data':None} if request.method=="GET": return render(request,'l4/login.html') if request.method=="POST": obj=login_form(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) ret['error']= obj.errors.as_data() return HttpResponse(json.dumps(ret,cls=newdumps))
7.2 情况二 验证成功,要去数据库中取值,queryset对象如何序列号,queryset对象内的时间格式如何序列化(2种方法)
7.2.1 django内置的序列号方法
from django.core import serializers v = models.tb.objects.all() data = serializers.serialize("json", v)
7.2.2 自定义cls来自定制json.dumps
import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field) v = models.tb.objects.values('id','name','ctime') v = list(v) v = json.dumps(v,cls=JsonCustomEncoder) ->datetime 还是需要处理
同样还是重构default方法,Python 中的isinstance函数,isinstance是Python中的一个内建函数。是用来判断一个对象的变量类型。
三、modelform
1、modelform的概括
modelform顾名思义有以下个方面的内容
1.1 数据库操作(表示其具有model的性质)
1.2 字段验证(表示其具有form的特性)
1.3 生成html(表示其具有form的特性)
2、 由model+form引出modelform的操作
2.1 model+form 添加或修改一条数据库内容的方式
2.1.1 model定义表,创建表
2.1.2 form定义对象
2.1.3 form在界面生成相应html标签
2.1.4 request返回后,进行is_vaild()验证
2.1.5 验证通过后生成cleaned_data交给model
2.1.6 model进行create或update操作
from django.shortcuts import render,HttpResponse # Create your views here. from django import forms from django.forms import fields from django.forms import widgets from app01 import models class UserInfo(forms.Form): username=fields.CharField(max_length=32) email=fields.EmailField() usertype_id=fields.ChoiceField( choices=[], widget=widgets.Select ) def __init__(self,*args,**kwargs): super(UserInfo, self).__init__(*args,**kwargs) self.fields['usertype_id'].choices=models.UserType.objects.values_list('id','caption') def login(request): if request.method=="GET": obj=UserInfo() print(obj.fields) return render(request,'login.html',{'obj':obj}) if request.method=="POST": obj=UserInfo(request.POST) if obj.is_valid(): res=obj.cleaned_data print(res) models.UserInfo.objects.create(**res) # models.UserInfo.objects.filter(id=1).update(**res) return HttpResponse('ok') else: print('xxxxx',obj.errors) return HttpResponse('no') def test(request): return render(request,'1.html') ************************** <body> <form action="/p1/login" method="post"> {% csrf_token %} {{ obj.as_p }} <input type="submit" value="提交"> </form> </body> ****************** from django.db import models # Create your models here. class UserType(models.Model): caption=models.CharField(max_length=32) class UserInfo(models.Model): username=models.CharField(max_length=32) email=models.EmailField() usertype=models.ForeignKey(to=UserType,to_field='id',on_delete=models.CASCADE) ******************
2.2 添加或修改一条数据库内容的方式
2.2.1 model定义表、创建表
2.2.2 定义modelform,继承model的表的字段
2.2.3 modelform界面生成html
2.2.4 request返回后,modelform直接is_vaild()
2.2.5 modelform直接save()保存
数据库的操作直接被封装起来了。同时,如果在取数据库记录的时候,相应的字段关联,也都会被自动取出。
现在,我们展开说modelform。
3、定义基本的modelform
class MYModelForm(forms.ModelForm) class Meta: model=models.UserInfo fields='__all__'
此时,html就已经定义好了。
def login2(request): if request.method=="GET": obj=UserInfo_modelform() return render(request,'login2.html',{'obj':obj}) --------------------------------- class UserInfo(models.Model): username=models.CharField(max_length=32) email=models.EmailField() usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE) u2g=models.ManyToManyField('UserGroup') ------------------------- class UserInfo_modelform(forms.ModelForm): class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' #需要展示哪些字段 --------------------------------------- <body> <form action="/p1/login2" method="post"> {% csrf_token %} {{ obj.as_p }} <input type="submit" value="提交"> </form> </body>
通过对比,我们可以发现,foreignkey及manytomany字段,modelform生成的html自动关联了相关数据
相比于form生成html,此处简便了非常多的操作
4、modelform中class Meta的一些参数
4.1 model #与之关联的model
4.2 fields=None, #显示哪些字段
class UserInfo_modelform(forms.ModelForm): class Meta: model=models.UserInfo #去哪个类里面获取字段 fields=['username'] #可以是列表
4.3 exclude #排除哪些字段
class UserInfo_modelform(forms.ModelForm): class Meta: model=models.UserInfo #去哪个类里面获取字段 exclude=['username']
4.4 verbose与label 显示中文
class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' labels={ 'username':'用户名', 'u2g':'所属组' } ----------------------------- class UserInfo(models.Model): username=models.CharField(max_length=32) email=models.EmailField(verbose_name='邮箱') usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE) u2g=models.ManyToManyField('UserGroup')
4.4 help_text 帮助信息
class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' labels={ 'username':'用户名', 'u2g':'所属组' } help_texts={ 'email':'xxx@xxx.com' }
4.5 插件定制 widgets
自定义插件 form中的插件,需要注意此处widgets与插件的widgets重名的情况 *
class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' labels={ 'username':'用户名', 'u2g':'所属组' } help_texts={ 'email':'xxx@xxx.com' } widgets={ 'username':Fwidgers.Textarea(attrs={'class':'c1'}) }
4.6 error_messages 自定义错误信息
自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' #fields=['username'] #可以是列表 #exclude=['username'] labels={ 'username':'用户名', 'u2g':'所属组' } help_texts={ 'email':'xxx@xxx.com' } widgets={ 'username':Fwidgers.Textarea(attrs={'class':'c1'}) } error_messages = { 'username': { 'max_length':"this writer name is too long" } }
4.7 field_classes=None # 自定义字段类 (也可以自定义字段) 将邮箱格式定义为url格式
4.8 localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 默认utc时间
4.9 定义models中没有的字段(一些不需要入数据库的字段,如一个月免登陆,等等)
class UserInfo_modelform(forms.ModelForm): extra_input=Ffields.CharField(max_length=32) class Meta: model=models.UserInfo #去哪个类里面获取字段 fields='__all__' #fields=['username'] #可以是列表 #exclude=['username'] labels={ 'username':'用户名', 'u2g':'所属组' } help_texts={ 'email':'xxx@xxx.com' } widgets={ 'username':Fwidgers.Textarea(attrs={'class':'c1'}) } error_messages = { 'username': { 'max_length':"this writer name is too long" } }
以上,生成html标签,样式的方式全部完毕
5、数据验证及保存
5.1 modelform与form的关系及数据验证的原理
Form->BaseForm-> is_valid
modelform->Base ModelForm->BaseForm-> is_vaild
is_vaild()是BaseForm的方法,所以modelform和form都有is_valid()方法。
5.2 modelform的数据保存
if obj.is_valid(): obj.save() #直接跳过models的操作 保存了数据 #一对多 多对多 直接帮我们跳过了所有的关联操作
6、modelform的钩子
与form相同,modelform的钩子函数与form一样,故modelform中也有3个钩子函数用来自定义数据验证(钩子函数写在modelform中)
7、数据的编辑与更新 instance的使用
->显示数据库数据直接将model对象放入 instance=model.obj
->更新 instance参数 obj=UserInfo_modelform(request.POST,instance=t_o)
def userdetail(request,nid): if request.method=="GET": obj=models.UserInfo.objects.filter(id=nid).first() mobj=UserInfo_modelform(instance=obj) return render(request,'userdetail.html',{'obj':mobj}) if request.method=="POST": t_o = models.UserInfo.objects.filter(id=nid).first() obj=UserInfo_modelform(request.POST,instance=t_o) if obj.is_valid(): obj.save() return redirect('/p1/userdetail-%s.html'%nid) else: return render(request,'/p1/userdetail-%s.html'%nid,obj)
8、obj.save(false)的理解
modelform为我们封装了很多数据库数据保存的操作。包括单表和多表,通过一个save都可以完成。
save()保存实际是包括了两个步骤
self.instance.save() #保存单表
self._save_m2m() #保存多表
if obj.is_valid(): #obj.save() x=obj.save(False) x.save() x.save_m2m()
9、回顾modelform流程
9.1 生成html class Meta
9.2 mf=xxxmodelform(instance=)
9.3 额外标签
9.4 各种验证 钩子 is_valid()
9.5 保存 save()
9.6 拆开
->saveinstance.save()
->mf.save_m2m()
四、原生ajax
1、背景
1.1 jquery中的ajax是对xhr(xmlHttpRequest)对象的封装
1.2 xhr对象在老版本的ie中不存在,当然,老版本ie中有类似的对象,在2.x/3.x版本的jquery中,不再支持老版本的ie,所以需要注意
2、原生ajax的理解
2.1 从socket角度理解原生ajax
->1、建立对象 b = new XMLHttpRequest() ->2、建立连接 open b.open(method,url,async) 连接时所需要信息 ->method POST、GET、DELETE... ->url ->async 同步还是异步(true为异步) ->3、发送内容 send() str ->4、设置请求头 ->set RequestHeader('key','value') ->5、获取所有响应头的内容 ->getALLResponseHeaders() ->6、获取某一个响应头的内容 ->getResponseHeader('key') ->7、abort()终止请求
2.2 从jquery $.ajax角度理解原生ajax
->$ajax( {url method data success } ) ->new ->open ->send ->get
3、原生ajax的基本操作一
建立连接,发送数据
b = new xmlHttpRequest() b.open('GET','http://127.0.0.1:8000',true) b.send('name=root,pwd='pwd')
print(request.POST)
4、原生ajax的基本操作二
4.1 理解状态码 b.readstatus
0-未初始化,尚未调用open()方法;
1-启动,调用了open()方法,未调用send()方法;
2-发送,已经调用了send()方法,未接收到响应;
3-接收,已经接收到部分响应数据;
4-完成,已经接收到全部响应数据;
4.2 理解回调函数 onreadystatechange
onreadystatechange=function(){}
当readState每次发生变化的时候执行此函数
if (b.readState == 4){} 做条件
4.3 理解获取返回值的方法
responseText 主要用Text 字符串数据 包括json数据
responseXML
4.4 知道状态码 b.states与b.statesText
b.status 404 b.readyState 4 b.responseText "<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Page not found at /index</title> <meta name="robots" content="NONE,NOARCHIVE"> <style type="text/css"> 省略 </style> </head> <body> 省略 </body> </html> " b.responseType "" b.statusText "Not Found" ->使用 return Httpresponse(‘xxx’,status=404,reason='Not Found')
补充httpresponse设置返回值的方法
def test(request): return HttpResponse('ok',status=404,reason='Not Found')
4.5 知道还原json数据的方法 JSON.parse(b.responseText)
x=[1,2,3] (3) [1, 2, 3] y=JSON.stringify(x); "[1,2,3]" JSON.parse(y); (3) [1, 2, 3]
4.6 知道如何获取响应头 getResponseHeader
b.open('GET','/p1/userdetail-3.html',true) b.send() b.getResponseHeader('cookie') null b.getResponseHeader('Content-Type') "text/html; charset=utf-8"
4.7 知道如何设置请求头
setRequestHeader('csrfToken','value') #csrf
4.8 理解post动作的处理
使用xhr发送post数据的时候需要设置请求头Content-Type
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset-UTF-8')
4.9 ie兼容性处理的方法
function GetXHR(){ var xhr = null; if(XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } return xhr; }
4.10 $.ajax()中获取xhr对象的方法
->$.ajax( { success:function(data,x1,,x2) } )
五、伪ajax
1、理解一 iframe标签的使用
iframe发送请求的方法
<body> <div>iframe测试</div> <iframe id='my_iframe' src="https://www.baidu.com"></iframe> <div><input id="input_url"></div> <div><input id='iframe_button' type="button" value="提交iframe"></div> <script src="jquery-1.12.4.js"></script> <script>
jquery
$('#iframe_button').click(function () { var tmp_url = $('#input_url').val(); $('#my_iframe').attr('src',tmp_url); }) </script> </body>
iframe可以通过src发送get请求且界面不刷新
2、理解二:利用iframe来提交表单
2.1 在form内添加一个iframe标签
2.2 给form标签添加一个target的属性 target=‘iframename’
2.3 form的method可以是get也可以是post都可以
2.4 submit这个form表单即可
post提交
<form action="/p1/xml_ajax" method="post" target="ifm1"> <iframe name="ifm1" id="ifm1"></iframe> <input type="text" name="name"> <input type="text" name="passwd"> <input type="submit"> </form>
get 提交
<form action="/p1/xml_ajax" method="get" target="ifm1"> <iframe name="ifm1" id="ifm1"></iframe> <input type="text" name="name"> <input type="text" name="passwd"> <input type="submit"> </form>
def xml_ajax(request): res={'state':True,'err':None,'data':None} if request.method=='GET': return HttpResponse('iframe-get') if request.method=="POST": print(request.POST) res['data']='ok' return HttpResponse(json.dumps(res))
3、理解三 :获取iframe的返回内容
3.1 iframe的onload事件的理解
对端返回完成后 触发onload
3.2 iframe返回值所存放的位置
3.3 获取document对象的方法
jquery
$('#ifm1').contents().find('body').text();
JavaScript
document.getElementById('ifm1').contentWindow.document.body.innerHTML
实例
<script src="/static/jquery-1.12.4.js"></script> <script> $('#ifm1').load(function () { console.log($('#ifm1').contents().find('body').text()); var tmp_text=$('#ifm1').contents().find('body').text(); var t_obj = JSON.parse(tmp_text); console.log(t_obj) }) </script>
4、发送普通数据(string)时,三种ajax方式的对比
普通数据:jquery>xmlHttpRequest->iframe
普通数据时,jquery是最方便最优先的方式
六、多种ajax对上传文件的处理
1、定义一个美观的上传界面的方法 (用一个button来覆盖住input file)
<div style="position: relative;height: 60px;width: 100px;" > <input type="file" id='fafafa' style="height: 60px;width: 100px; z-index: 90;position: absolute;top:0;bottom: 0;right: 0;left: 0;opacity: 0"> <a style="border-radius: 20px;text-align: center;line-height: 60px;z-index: 50;height: 60px;width: 100px;background-color: deepskyblue;border: 1px solid darkblue;position: absolute;top:0;bottom: 0;right: 0;left: 0">上传</a> </div>
2、获取<input type='file'>的文件对象
var fobj=document.getElementById('fafafa').files[0] f.obj.name//可以获取文件名
3、原生xhr发送文件
3.1 依赖FormData()对象 FormDate().append(key,value)
3.2 不需要添加POST头
<input type="file" id="input_file"> <input type="button" value="XmlHttpRequest发送" id="xhr_button"> ----------------------------- $('#xhr_button').click( function () { var file_obj=document.getElementById('input_file').files[0]; var xhr_connect=new XMLHttpRequest(); xhr_connect.open('POST','/p1/receive_file',true); var tmp_data=new FormData(); tmp_data.append('k1','123'); tmp_data.append('realfile',file_obj); xhr_connect.onreadystatechange=function () { if (xhr_connect.readyState===4){ console.log(xhr_connect.responseText); } }; xhr_connect.send(tmp_data); } );
$('#xhr_button').click( function () { var file_obj=document.getElementById('input_file').files[0]; var xhr_connect=new XMLHttpRequest(); xhr_connect.open('POST','/p1/receive_file',true); var tmp_data=new FormData(); tmp_data.append('k1','123'); tmp_data.append('csrfmiddlewaretoken','{{ csrf_token }}'); tmp_data.append('realfile',file_obj); xhr_connect.onreadystatechange=function () { if (xhr_connect.readyState===4){ console.log(xhr_connect.responseText); } }; xhr_connect.send(tmp_data); } );
formdata不仅能携带文件,还能发送参数。
4、jquery ajax发送文件
->同样依赖FormDatad对象
->添加两个参数
processData:false, // tell jquery not to process the data
contentType:false, //tell jquery not to set contentTYPE
$.ajax({
data:f_obj
processData:false, // tell jquery not to process the data
contentType:false, //tell jquery not to set contentTYPE
})
$('#ajax_button').click(function () { var file_obj=document.getElementById('input_file').files[0]; var tmp_data=new FormData(); tmp_data.append('k1','123'); tmp_data.append('csrfmiddlewaretoken','{{ csrf_token }}'); tmp_data.append('realfile',file_obj); $.ajax( { url:'/p1/receive_file', type:'POST', data:tmp_data, processData:false, // tell jquery not to process the data contentType:false, //tell jquery not to set contentTYPE success:function (receive_data,a1,a2) { console.log(receive_data); console.log(a1); console.log(a2); } } ) });
注:formdata在ie浏览器中不兼容
5、iframe 发送文件
form 中 enctype="multipart/form-data 添加<input type='file'>其他和发普通请求完全一致
<form action="/p1/receive_file" method="post" target="ifm1" enctype="multipart/form-data"> {% csrf_token %} <iframe name="ifm1" id="ifm1" style="display: none"></iframe> <input type="text" value="123" name="k1" style="display: none"> <input type="file" name="realfile"> <input type="submit" id="iframe_button" value="iframe提交"> </form> ------------------------------------- $('#iframe_button').click( function () { $('#ifm1').load( function () { console.log('iframe_ok') } ); } );
6、ajax文件传输归纳
由于formdata对ie浏览器不支持,所以ajax传文件的优先级为
iframe>jquery>原生ajax
7、图片预览
7.1 input的file标签onload触发 iframe的submit
7.2 返回值 $('iframename').contents().find('body').text()获取图片path
7.3 在相应位置插入图片
var img_obj=document.createElement('img');
img_obj.src=tmp_path;
$('#pre_view').empty().append(img_obj);
<form action="/p1/receive_pic" method="post" target="ifm2" enctype="multipart/form-data" id="pic_form"> {% csrf_token %} <iframe name="ifm2" id="ifm2" style="display: none"></iframe> <input type="file" name="user_pic" id="user_pic"> </form> -------------------------------- $('#ifm2').load(function () { tmp_path=$('#ifm2').contents().find('body').text(); tmp_path='/'+tmp_path; var img_obj=document.createElement('img'); img_obj.src=tmp_path; //img_obj.style.width='200px'; //img_obj.style.height='400px'; $('#pre_view').empty().append(img_obj); }); -------------------------------- def receive_pic(request): if request.method=="POST": y=request.FILES.get('user_pic') y_name=str(y.name) pic_path=os.path.join('static/image/',y.name) print(pic_path) f=open(pic_path,'wb') for i in y.chunks(): f.write(i) return HttpResponse(pic_path)
七、随机验证码生成
1、分析
1.1 一个url显示网页框架,一个url用来显示图片给img标签
1.2 不同的人,不同的验证码,验证码需要和session相关联
1.3 使用一函数,生成图片和验证码内容,每次单击图片,更新图片及更新session
1.4 用户post登录,验证输入的验证码和session中的验证码是否一致
1.5 img图片只有修改路径后才会刷新,如何解决。
2、实现
2.1 pillow函数生成图片及字符串,我们可以将图片保存在本地并rb后Httpresponse给前方,或者直接写在BIOSIO中,省去保存图片到本地的操作,此过程实现细节可以不做深入,直接调用
2.2 session写入随机验证码
2.3 图片返回前端
2.4 img对象的src=this.src+='?'
#html页面 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="text" placeholder="验证码" id="code_auth_input"> <img src="p1/check_code_img" id="code_auth_img"> <div id="auth_result"></div> </body> <script src="/static/jquery-1.12.4.js"></script> <script> document.getElementById('code_auth_img').onclick=function () { console.log(this.src); this.src+='?'; }; document.getElementById('code_auth_input').onchange=function () { console.log(this.value); $.ajax( { url:'{{ request.path_info }}', type:'POST', data:{'auth_code':this.value}, success:function (receive_data) { console.log(receive_data); var t_span=document.createElement('span'); if (receive_data=='1'){ t_span.textContent='验证码认证成功'; t_span.style.color='green'; }else{ t_span.textContent='认证失败'; t_span.style.color='red'; } $('#auth_result').empty().append(t_span); }, } ); }; </script> </html> #url的请求过程 path('code_auth_login',views.code_auth_login), re_path('check_code_img(\w*)',views.check_code_img),#如果path不变化,图片不会刷新 #step 1 view对验证码连接的处理,生成验证吗并保存session def check_code_img(request,*args,**kwargs): t_img,t_str=create_validate_code()#此函数不展开 f=BytesIO() t_img.save(f,'PNG')#将图片保存到内存中 request.session['check_code']=t_str return HttpResponse(f.getvalue())#将图片已二进制方式返回前端 #step2 view对页面请求的处理 def code_auth_login(request): if request.method=="GET": return render(request,'code_auth_login.html') if request.method=="POST": #验证码验证 t_auth_code=request.POST.get('auth_code') if t_auth_code==request.session['check_code']: return HttpResponse('1') else: return HttpResponse('0')
3、附录 验证码图片生成的代码
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="Monaco.ttf", length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance=2): """ @todo: 生成验证码图片 @param size: 图片的大小,格式(宽,高),默认为(120, 30) @param chars: 允许的字符集合,格式字符串 @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG @param mode: 图片模式,默认为RGB @param bg_color: 背景颜色,默认为白色 @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF @param font_size: 验证码字体大小 @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf @param length: 验证码字符个数 @param draw_lines: 是否划干扰线 @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 @param draw_points: 是否画干扰点 @param point_chance: 干扰点出现的概率,大小范围[0, 100] @return: [0]: PIL Image实例 @return: [1]: 验证码图片中的字符串 """ width, height = size # 宽高 # 创建图形 img = Image.new(mode, size, bg_color) draw = ImageDraw.Draw(img) # 创建画笔 def get_chars(): """生成给定长度的字符串,返回列表格式""" return random.sample(chars, length) def create_lines(): """绘制干扰线""" line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num): # 起始点 begin = (random.randint(0, size[0]), random.randint(0, size[1])) # 结束点 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): """绘制干扰点""" chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width): for h in range(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): """绘制验证码字符""" c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs if __name__=='__main__': x,y=create_validate_code() print(x,y)
八、分类搜索
1、阶段一 创建数据库内容,并在界面全部展示
1.1 建立表格及关系
->文章表
->文章主题表(网站给定不能新增)
->自定义分类表(用户)
1.2 添加数据
from django.db import models # Create your models here. class CustomType(models.Model): #用户自定义分类,可以动态变化 typename=models.CharField(max_length=32) class ArticleItem(models.Model): #网站固定主题,不可变 itemname=models.CharField(max_length=32) class Article(models.Model): #用户的文章,包括标题,内容 title=models.CharField(max_length=64) content=models.CharField(max_length=256) custom_type=models.ForeignKey(to='CustomType',to_field='id',on_delete=models.CASCADE) article_item=models.ForeignKey(to='ArticleItem',to_field='id',on_delete=models.CASCADE)
1.3 将所有内容返回界面显示
#列出所有文章 all_article=models.Article.objects.all() #列出所有的主题 all_item=models.ArticleItem.objects.all() #列出所有的分类 all_type=models.CustomType.objects.all()
1.4 界面循环显示
2、阶段二 后台获取过滤后文章列表的方法,并定义获取全部文章的方法
2.1 后台获取过滤后文章的方法
定义两个变量,分别代表文章主题的id,和自定义分类的id,为0,则表示all()
models.xxx.objects.filter(x_id=1,y_id=2) #字典作为过滤条件 models.xxx.objects.filter(**kwargs)
定义0为all()
filter_item = {} #这里偷懒了,实际应该获取相应的key,可能存在未知bug #但是能匹配上正则的,应该此处不会有问题。先这么写吧 for i,j in kwargs.items(): if j=='0': pass else: filter_item[i]=j #过滤出要显示的文章 all_article=models.Article.objects.filter(**filter_item) #列出所有的主题 all_item=models.ArticleItem.objects.all() #列出所有的分类 all_type=models.CustomType.objects.all()
3、url处理及web过滤连接的生成
3.1 url 正则处理 将kwargs处理为x_id y_id
re_path('article_search/(?P<article_item_id>\d*)-(?P<custom_type_id>\d*).html',views.article_search), re_path('article_search(.*)',views.article_search),
3.2 完成后端
除了返回前端上面的3种数据外,还需要返回当前的过滤条件。方便前端显示相应的效果,并生成相应的a标签url
#字典为空的时候 如第一次访问的情况处理 if not filter_item.get('article_item_id'): filter_item['article_item_id']=0 if not filter_item.get('custom_type_id'): filter_item['custom_type_id']=0 #让字典内的key对应的都是int #实际if 可以不写 for i,j in kwargs.items(): if j=='0': filter_item[i]=0 else: filter_item[i]=int(j)
3.3 构造a标签的url,需要注意全部的0的处理,以及被选定的过滤条件的样式变化
<div id="search_line"> <div> <span>文章主题</span> {% if s_dic.article_item_id == 0 %} <a class="be_selected" href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a> {% else %} <a href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a> {% endif %} {% for i in all_item %} {% if i.id == s_dic.article_item_id %} <a class="be_selected" href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a> {% else %} <a href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a> {% endif %} {% endfor %} </div> <hr> <div> <span>自定义分类</span> {% if s_dic.custom_type_id == 0 %} <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a> {% else %} <a href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a> {% endif %} {% for i in all_type%} {% if i.id == s_dic.custom_type_id %} <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a> {% else %} <a href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a> {% endif %} {% endfor %} </div> </div>
4、完整代码
4.1 html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <style> span{ display: inline-block; width: 100px; margin: 0 20px; text-align: center; } #search_line a{ display: inline-block; width: 100px; border: 1px solid red; margin: 0 20px; text-decoration: none; text-align: center; } .be_selected{ background-color: pink; color: palegoldenrod; } </style> <body> <h1>分类查询</h1> <div id="search_line"> <div> <span>文章主题</span> {% if s_dic.article_item_id == 0 %} <a class="be_selected" href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a> {% else %} <a href="/blog/article_search/0-{{ s_dic.custom_type_id}}.html">全部</a> {% endif %} {% for i in all_item %} {% if i.id == s_dic.article_item_id %} <a class="be_selected" href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a> {% else %} <a href="/blog/article_search/{{ i.id }}-{{ s_dic.custom_type_id}}.html">{{ i.itemname }}</a> {% endif %} {% endfor %} </div> <hr> <div> <span>自定义分类</span> {% if s_dic.custom_type_id == 0 %} <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a> {% else %} <a href="/blog/article_search/{{ s_dic.article_item_id }}-0.html">全部</a> {% endif %} {% for i in all_type%} {% if i.id == s_dic.custom_type_id %} <a class="be_selected" href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a> {% else %} <a href="/blog/article_search/{{ s_dic.article_item_id }}-{{i.id}}.html">{{ i.typename }}</a> {% endif %} {% endfor %} </div> </div> <h1>查询结果</h1> {% for i in all_art %} <div> <a href="{{ request.path_info }}" style="text-decoration: none">{{ forloop.counter }}--{{ i.title }}</a> </div> {% endfor %} </body> </html>
4.2 view函数
def article_search(request,*args,**kwargs): filter_item = {} #这里偷懒了,实际应该获取相应的key,可能存在未知bug #但是能匹配上正则的,应该此处不会有问题。先这么写吧 for i,j in kwargs.items(): if j=='0': pass else: filter_item[i]=j #过滤出要显示的文章 all_article=models.Article.objects.filter(**filter_item) #列出所有的主题 all_item=models.ArticleItem.objects.all() #列出所有的分类 all_type=models.CustomType.objects.all() #字典为空的时候 如第一次访问的情况处理 if not filter_item.get('article_item_id'): filter_item['article_item_id']=0 if not filter_item.get('custom_type_id'): filter_item['custom_type_id']=0 #让字典内的key对应的都是int #实际if 可以不写 for i,j in kwargs.items(): if j=='0': filter_item[i]=0 else: filter_item[i]=int(j) return render(request,'article_search.html',{'all_art':all_article,'all_item':all_item,'all_type':all_type,'s_dic':filter_item})
九、ajax跨域--jsonp
1、 json与jsonp的区别
json是一种数据类型 字符串 jsonp是一种方式
2、requests模块的基本使用 (不做深入了解)
2.1 response=request.get('https://www.baidu.com')
2.2 respense.content字节
2.3 response.encoding='utf-8'
2.4 response.text 页面内容content字节类型被encoding后
2.5 response.cookies 查看cookies
2.6 response.headers 查看headers
x=requesets.get('http://www.qq.com') >>> print(x.headers) {'Date': 'Thu, 11 Oct 2018 09:34:34 GMT', 'Content-Type': 'text/html; charset=GB2312', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'squid/3.5.24', 'Vary': 'Accept-Encoding, Accept-Encoding, Accept-Encoding', 'Expires': 'Thu, 11 Oct 2018 09:35:35 GMT', 'Cache-Control': 'max-age=60', 'Content-Encoding': 'gzip', 'X-Cache': 'MISS from tianjin.qq.com'} >>> print(x.cookies) <RequestsCookieJar[]> >>> print(x.encoding) GB2312 >>> print(type(x.text)) <class 'str'> >>> print(type(x.content)) <class 'bytes'> >>> response1 = requests.get("https://fanyi.baidu.com") >>> print(response1.cookies) <RequestsCookieJar[<Cookie BAIDUID=C07CA5D297A71A59C02A1F223AF0DCDE:FG=1 for .baidu.com/>, <Cookie locale=zh for .baidu.com/>]> >>> requests.get("http://127.0.0.1:8000/p1/check_code_img").cookies <RequestsCookieJar[Cookie(version=0, name='sessionid', value='958utmw44tpj73v5iavf7w6fhpr82xku', port=None, port_specified=False, domain='127.0.0.1', domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=1540703854, discard=False, comment=None, comm ent_url=None, rest={'HttpOnly': None, 'SameSite': 'Lax'}, rfc2109=False)]>
2.7 requeset发送带参数的get和post
#get import requests params = { "wd": "python", "pn": 10, } response = requests.get('https://www.baidu.com/s', params=params) print(response.url) print(response.text) ------------------------------- #POST import requests post_data = {'username': 'value1', 'password': 'value2'} response = requests.post("http://xxx.com/login/", data=post_data) --------------------------------- #hearders import requests headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/" "537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" } response1 =requests.get("https://www.baidu.com", headers=headers) response2 =requests.post("https://www.xxxx.com", data={"key": "value"}, headers=headers) print(response1.headers) print(response1.headers['Content-Type']) print(response2.text)
我们要给其他网站发送相应请求信息的的时候,浏览器给我们的server,我们的server转发请求。此时界面可以返回正常的数据。
3、跨域后浏览器出现的阻塞现象(浏览器的同源策略)
实际请求流量和返回流量都已经完成,但是浏览器的同源策略阻塞这种跨域流量
4、jsonp的解决思路
4.1 基本思路
4.1.1 使用script的src来get远端数据
4.1.2 script的srcget到的数据必须是js能读懂的内容。
4.1.3 远端返回的内容执行callback函数,将具体内容作为参数传到callback函数中。
4.1.4 html中需要定义callback函数,以及读取内容后的处理过程
5、 实现方式一: 定义script标签修改src
function list(x) { console.log(x); } document.getElementById('jsonp_button').onclick=function () { var jsonp_obj=document.createElement('script'); jsonp_obj.src='http://www.jxntv.cn/data/jmd-jxtv2.html'; document.getElementById('1').appendChild(jsonp_obj); document.getElementById('1').removeChild(jsonp_obj); };
注意,此处我们定义的callback函数为list。才专门提供jsonp接口的网址,callback函数的名称会根据你get请求时,callback的值来确定。
如:https://api.douban.com/v2/book/search?callback=list&q=javascript&count=1
https://api.douban.com/v2/book/search?callback=niubi&q=javascript&count=1
6、实现方式二 $.ajax封装生成script标签的过程
type->POST / datatype->jsonp / jsonp->callback / jsonpcallback->list
$('#jsonp_ajax_button').click(function () { $.ajax( { url:'https://api.douban.com/v2/book/search?q=javascript&count=1', type:'POST', dataType:"jsonp", jsonp:'callback', jsonpCallback: 'list' } ) });
ajax此时会自己生成一个script标签,把src放进去。执行后删除
7、实现方式三 CORS
CORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。(较少用,不展开,了解就行)
8、再提供几个jsonp的接口
http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301 http://www.weather.com.cn/data/sk/101110101.html >>> import requests >>> requests.get('http://www.weather.com.cn/data/sk/101110101.html') <Response [200]> >>> x=requests.get('http://www.weather.com.cn/data/sk/101110101.html') >>> print(x.text) {"weatherinfo":{"city":"西å®","cityid":"101110101","temp":"23.3","WD":"西åé£","WS":"å°äº3级","SD":"52%","AP":"962.7hPa","njd":"ææ å®åµ","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}} >>> print(x.encoding) ISO-8859-1 >>> x.encoding='utf-8' >>> print(x.text) {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}} >>>
十、kindeditor
1、阶段一 基本使用
->下载kindeditor
->放入static文件夹下
->定义一个textarea 设置id mykindeditor
->引入jquery
->进入kindeditor中的kindeditor-all.js
->KindEditor.create('#mykindeditor',{});
->完成 此时界面出现kindeditor框
<textarea id="content" style="width: 100px"> <script> KindEditor.create('#content',{}); </script>
2、阶段二 基本参数
width:'100%',//可以像素,可以百分比
height:'300px',//只能像素
minWidth:'200px',//只能像素
minHeight:'100px'//只能像素
<body> <textarea id="content" style="width: 100px"> </textarea> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> function initKindEditor() { var kind = KindEditor.create('#content',{ width:'100%', height:'300px', minWidth:'200px', minHeight:'100px' }); } $(function () { initKindEditor(); }); </script> </body>
3、阶段三 了解一大堆参数
//1 显示哪些 item:[] //一个数组,显示哪些工具 //2 可用哪些 noDisableItems:[] designMode 为false时,要保留的工具栏图标。 // 3 xss过滤 filterMode=true true时根据 htmlTags 过滤HTML代码,false时允许输入任何代码。 数据类型: Boolean 默认值: true // 4 设置kindeditor能否被拖动 resizeType 有3个值 2 1 0 2或1或0,2时可以拖动改变宽度和高度,1时只能改变高度,0时不能拖动。 // 5 禁用鼠标 useContextmenu // 6 syncType 同步数据的方式,可设置”“、”form”,值为form时提交form时自动同步,空时不会自动同步。 //7 autoHeightMode 自动增高 //kindeditor还有很多参数 此处不做展开,以上参数已经能完成大部分功能
4、kindeditor文件处理
4.1 上传路径定义 uploadJson
4.2 文件变量的参数定义(request.files.get(?)) filePostName
4.3 csrf处理 extraFileUploadParams
<script> function initKindEditor() { var kind = KindEditor.create('#content',{ width:'100%', height:'300px', minWidth:'200px', minHeight:'100px', uploadJson:'/p1/kind_upload_pic',//定义url extraFileUploadParams:{ 'csrfmiddlewaretoken':"{{ csrf_token}}" },//csrf处理 filePostName:'fafafa'//文件对象定义 }); } $(function () { initKindEditor(); });//页面框架加载完成运行 </script> ------------------------------------ def kind_upload_pic(request): dic={ 'error':0, 'url':'/static/image/Desert.jpg', 'message':None } if request.method=="POST": print(request.FILES.get('fafafa').name) return HttpResponse(json.dumps(dic))
4.4 文件类型如何区分
可以通过request.method.get 获取相应的数据
十一、xss过滤
1、生成界面
1.1 界面模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>后台管理</title> <style> .pg-header{ position: fixed; top: 0; right: 0; left: 0; height: 48px; background: #2459a2; } .pg-header .logo{ float: left; color:white; line-height: 48px; } .pg-header .login_info{ float: right; color: white; line-height: 48px; } .pg-header .header-centerctl{ width: 80%; margin: 0 auto; } .header-centerctl span{ margin: 2px; } .left { float: left; } .right{ float: right; } .pg-content{ margin-top: 48px; } .pg-footer{ height: 20px; background: #dddddd; text-align: center; line-height: 20px; position: fixed; bottom: 0; left: 0; right: 0; } .pg-content .menu{ position: fixed; top:48px; bottom: 20px; left: 0; width: 200px; overflow: auto; } .pg-content .content{ position: fixed; top:48px; bottom: 20px; right: 0; left: 200px; background: lightgoldenrodyellow; overflow: auto; } .pg-content .menu div{ height: 50px; text-align: center; line-height: 50px; } .pg-content .menu div:hover{ font-size: 20px; background-color: blueviolet; } </style> {% block css %} {% endblock %} </head> <body style="margin: 0 auto ;width: 1000px" > <div class="pg-header"> <div class="header-centerctl"> <div class="logo">简陋的后台管理</div> <div class="login_info"> <span>头像</span> <span>登录</span> <span>退出</span> </div> </div> <div style="clear: both"></div> </div> <div class="pg-content"> <div class="menu left"> <div style="background: #dddddd">菜单</div> <div>list1</div> <div>list2</div> <div>list3</div> <div>list4</div> <div>list5</div> <div>list6</div> <div>list7</div> <div>list8</div> <div>list9</div> <div>list10</div> <div>list1</div> <div>list2</div> <div>list3</div> <div>list4</div> <div>list5</div> <div>list6</div> <div>list7</div> <div>list8</div> <div>list9</div> <div>list10</div> </div> <div class="content right"> <div style="min-width: 1000px"> {% block content%} {% endblock %} </div> </div> <div style="clear: both"></div> </div> <div class="pg-footer">@2018 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':'1','article_item_id':'1'})#定义默认值 return render(request, 'xss_edit_mode.html', {'x': x})
1.4 生成完整界面
{% extends "xss_template.html" %} {% block content%} <form action="{{ request.path_info }}" method="post"> {% csrf_token %} <div style="margin: 0 auto;width: 90%"> <p>标题{{ x.errors.title.0 }}</p> {{ x.title }} <p>正文{{ x.errors.content.0 }}</p> {{ x.content }} <p>文章主题{{ x.errors.article_item_id.0 }}</p> {{ x.article_item_id }} <p>文章分类{{ x.errors.custom_type_id.0 }}</p> {{ x.custom_type_id }} <input type="submit" name="提交" value="提交"> </div> </form> {% endblock %} {% block js %} <script src="/static/jquery-1.12.4.js"></script> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create('#kind_textarea',{}); //form定义的时候已经定义好了id </script> {% endblock %}
2、后端获取form内容
常规操作,form(request.POST),is_valid(),cleaned_data()
if request.method=="POST": x=article_form(request.POST) if x.is_valid(): x=x.cleaned_data x['content']=filter_content(x['content']) return render(request, 'xss_edit_show.html', {'x': x}) else: print('error!!!') return render(request,'xss_edit_mode.html',{'x':x})
3、内容过滤的基本方法
3.1 提交过来的内容格式及显示效果
content的内容实际就是html标签。
<h1> 测试一下效果<br /> </h1> <h2> <u>哈哈哈</u> </h2> <h3> 啦啦啦 </h3> <p> <span>来个图片<span style="font-size:14px;"><img src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif" border="0" alt="" /></span></span> </p>
3.2 beautifulsoup4对文本标签的处理
3.2.1 获取被处理后的内容对象 BeautifulSoup(input_content, 'html.parser')
tmp_content_obj = BeautifulSoup(input_content, 'html.parser')
3.2.2 输出对象内所有的标签(包括嵌套在标签内部的标签)
print(tmp_content_obj.find_all()) ------------------------------------------------ [<h1>测试一下效果<br/></h1>, <br/>, <h2><u>哈哈哈</u></h2>, <u>哈哈哈</u>, <h3>啦啦啦</h3>,
<p><span>来个图片<span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span></span></p>,
<span>来个图片<span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span></span>,
<span style="font-size:14px;"><img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/></span>,
<img alt="" border="0" src="http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif"/>]
循环输出标签名字
for i in tmp_content_obj.find_all(): print(i.name) ---------------------- h1 br h2 u h3 p span span img
循环输出标签属性
for i in tmp_content_obj.find_all(): print(i.name,i.attrs) ------------------------ h1 {} br {} h2 {} u {} h3 {} p {} span {} span {'style': 'font-size:14px;'} img {'src': 'http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif', 'border': '0', 'alt': ''}
清除标签内内容的方法,标签依然在,只是内容清空
for i in tmp_content_obj.find_all(): i.clear() print(i,i.name,i.attrs) ---------------------------- <h1></h1> h1 {} <br/> br {} <h2></h2> h2 {} <u></u> u {} <h3></h3> h3 {} <p></p> p {} <span></span> span {}
清除内容且清除标签的方法 hidden = True
for i in tmp_content_obj.find_all(): i.clear() i.hidden = True print(i,i.name,i.attrs) ------------------------------------------- h3 {} p {} span {} span {'style': 'font-size:14px;'} img {'src': 'http://127.0.0.1:8000/static/kindeditor-4.1.10/plugins/emoticons/images/28.gif', 'border': '0', 'alt': ''}
清除标签某个属性的方法(删除字典内容的方法)
del i.attrs[t_attr]
4、定义白名单
white_list_dict={ "font": ['color', 'size', 'face', 'style'], 'b': [], 'div': [], "span": ['style'], "table": [ 'border', 'cellspacing', 'cellpadding' ], 'th': [ 'colspan', 'rowspan' ], 'td': [ 'colspan', 'rowspan' ], "a": ['href', 'target', 'name'], "img": ['src', 'alt', 'title'], 'p': [ 'align' ], "pre": ['class'], "hr": ['class'], 'strong': [], 'h1': [], 'h2': [] }
5、实例
#step1 建立白名单 white_list_dict={ "font": ['color', 'size', 'face', 'style'], 'b': [], 'div': [], "span": ['style'], "table": [ 'border', 'cellspacing', 'cellpadding' ], 'th': [ 'colspan', 'rowspan' ], 'td': [ 'colspan', 'rowspan' ], "a": ['href', 'target', 'name'], "img": ['src', 'alt', 'title'], 'p': [ 'align' ], "pre": ['class'], "hr": ['class'], 'strong': [], 'h1': [], 'h2': [] } #step2 创建Beautifulsoup类 from bs4 import BeautifulSoup def filter_content(input_content): tmp_content_obj = BeautifulSoup(input_content, 'html.parser') #step3 寻找某标签, #step4 寻找标签可以用find 这个只能找出一个 即使内容中包含很多 #step5 可以通过clear()hidden() attrs 来删除标签 删除内容或 针对属性做del或修改 #tag=tmp_content_obj.find('script') #print(tag,type(tag)) ##针对取出的bs4.element.Tag对象可以进行clean操作将其清空 #tag.clear() # ##将修改后的对象重新转换为文本保存 ## 返回前端 <script></script>只清空内部内容,不会清空script这个标签 #tag.hidden = True #会连<script>一起清空 # #tag2=tmp_content_obj.find('h1') #print(tag2.attrs) #tag2.attrs['style']='color:red' print('xxx',tmp_content_obj.find_all()) for i in tmp_content_obj.find_all(): print(i.name) for i in tmp_content_obj.find_all(): i.clear() i.hidden = True print(i,i.name,i.attrs) for t_tag in tmp_content_obj.find_all(): if t_tag.name in white_list_dict: tmp_attrs_dict = t_tag.attrs tmp_white_attr_list = white_list_dict[t_tag.name] for t_attr in list(tmp_attrs_dict): if t_attr in tmp_white_attr_list: pass else: del tmp_attrs_dict[t_attr] else: t_tag.clear() t_tag.hidden = True return tmp_content_obj.decode()
6、界面显示
if x.is_valid(): x=x.cleaned_data x['content']=filter_content(x['content']) return render(request, 'xss_edit_show.html', {'x': x}) {% extends "xss_template.html" %} -------------------------------- {% block content%} <form action="{{ request.path_info }}" method="post"> <div style="margin: 0 auto;width: 90%"> {% csrf_token %} {{ x.title }} <hr> {{ x.content | safe }} </div> </form> {{ x.errors }} {% endblock %} {% block js %} <script src="/static/jquery-1.12.4.js"></script> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create('#kind_textarea',{}); </script> {% endblock %}
最终效果
十二、单例模式
1、两种单例模式
1.1
class foo1(object): instance=None def __init__(self): self.name='foo1' @classmethod def get_instance(self): if foo1.instance: return foo1.instance else: foo1.instance=foo1() return foo1.instance def process(self,x): print(x) obj1=foo1.get_instance() obj2=foo1.get_instance() print(obj1,obj2) obj1.process(id(obj1)) obj2.process(id(obj2))
<__main__.foo1 object at 0x000000000220EEB8> <__main__.foo1 object at 0x000000000220EEB8> 35712696 35712696 35815496 35815496
1.2
class foo2(object): instance=None def __new__(cls, *args, **kwargs): if foo2.instance: return foo2.instance else: foo2.instance=object.__new__(cls,*args,**kwargs) return foo2.instance def __init__(self): self.name='foo2' x1=foo2() x2=foo2() print(id(x1),id(x2))
2、全局变量和单例模式的区别(了解)
2.1 全局变量是对一个对象的静态引用,全局变量确实可以提供单例模式实现的全局访问功能,但是它并不能保证应用程序只有一个实例;编码规范也明确的指出应该要少使用全局变量,因为过多的使用全局变量会造成代码难读;全局变量并不能实现继承。
2.2 单例模式虽然在继承上不能很好的处理,但是还是可以实现继承的;单例模式在类中保存了它的唯实例这个类,可以保证只能创建一个实例,同时它还提供了一个访问该唯一实例的全局访问点。
附录:大纲
对比认识model、Form及modelForm ->model的功能 ->创建数据库表 ->单表 class xxx(models.MODEL) ->创建表 ->定义字段 ->charField ->EmailField ->IntegerField ->IntegerField(choices=)#此choices要和form中的choices分开 ->error_message 与form中的error_message分开 ->定制元信息 class Meta: ->定制表名 ->db_table='tb1'#数据库中表名就叫tb1,不再是默认的app+下划线+类名 ->联合(唯一)索引 ->普通索引 db_index=true 加快查找速度(数据库中会针对每个索引单独创建一个文件) ->index_together 联合索引 ->index_together=[('name','sex'),] ->最左前缀 ->select * from where name=xxx 命中 ->select * from where name=xxx and sex=xxx 命中 ->select * from where sex 不能命中 ->unique_together 联合唯一索引 ->unique_together=[('name','sid'),] ->遵循最左前缀,同时组合只能唯一 ->verbose_name='admin名称' verbose_name_pluar='admin名称复数' ->多表操作 ->ForeignKey ->foreginkey的实质 ->表示关系 ->约束 ->models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users') ->OnetoOne ->OneToONe的本质 ->foreginkey的基础 ->唯一约束 ->ManyToMany ->ManyToMany的本质 ->两张表基础上延伸出来第三张表 ->两张表双向的foreginkey ->用第三张表来保存双向关系 ->关联情况下的删除操作 ->两张表 ->用户表 ->用户类型表 ->删除用户类型 ->原始sql会报错 ->django中 models.UserType.objects.filter(id=1).delete() ->早期django会报错 ->现在django会把相关联的数据全部删除 ->on_delete的参数 ->on_delete的参数 -> models.CASCADE,删除关联数据,与之关联也删除 -> models.DO_NOTHING,删除关联数据,引发错误IntegrityError 数据库抛出异常 -> models.PROTECT,删除关联数据,引发错误ProtectedError django抛出异常 -> models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) -> models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) -> models.SET,删除关联数据, a. 值 与之关联的值设置为指定值,设置:models.SET(值) b. 函数(返回值) 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) ->一对多的正向、反向查找 ->正向 ->点 ->双下划线 s4=models.dev_info.objects.filter(did__lt=5) s5=models.dev_info.objects.filter(did__lt=5).values('dev_ip','dev_port','dev_type__type_name') s6=models.dev_info.objects.filter(did__lt=6).values_list('dev_ip','dev_port','dev_type__type_name') ->反向 ->点与set 格式 b.a_set ->例子:通过dev_type找相应的dev_info -> s7 = models.devtype_table.objects.all() for i in s7: print(i.type_name,i.dev_info_set.all()) ----------------------------------------- 路由器 <QuerySet [<dev_info: dev_info object (3)>, <dev_info: dev_info object (12)>, <dev_info: dev_info object (21)>, <dev_info: dev_info object (23)>]> 交换机 <QuerySet [<dev_info: dev_info object (5)>, <dev_info: dev_info object (17)>]> 防火墙 <QuerySet [<dev_info: dev_info object (6)>]> 服务器 <QuerySet [<dev_info: dev_info object (4)>, <dev_info: dev_info object (11)>]> ->双下划线 values('a__id') ->此处不要加set -> s8 = models.devtype_table.objects.all().values('type_name','dev_info__dev_ip','dev_info__dev_port') for i in s8: print(i) ----------------------------------------------------------------- {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.1', 'dev_info__dev_port': '44568'} {'type_name': '路由器', 'dev_info__dev_ip': '5.77.3.33', 'dev_info__dev_port': '666'} {'type_name': '路由器', 'dev_info__dev_ip': '11122', 'dev_info__dev_port': '1111'} {'type_name': '路由器', 'dev_info__dev_ip': '1.1.1.2222', 'dev_info__dev_port': '123'} {'type_name': '交换机', 'dev_info__dev_ip': '3.3.3.3', 'dev_info__dev_port': '80'} {'type_name': '交换机', 'dev_info__dev_ip': '1.1.1.222', 'dev_info__dev_port': '200'} {'type_name': '防火墙', 'dev_info__dev_ip': '4.4.4.4', 'dev_info__dev_port': '443'} {'type_name': '服务器', 'dev_info__dev_ip': '2.2.2.2', 'dev_info__dev_port': '80'} {'type_name': '服务器', 'dev_info__dev_ip': '5.6.7.8', 'dev_info__dev_port': '1444'} ->表格自关联的时候注意的事项 related_name 可以将xxx_set重命名为 新的名字 xxx_set==a related_query_name 可以将xxx重命名为新的民资 如b_set==xxx_set ->多对多的正向反向操作 ->正向 a. django创建第三张表 m2m.remove m2m.add m2m.set m2m.clear m2m.filter() b. 自定义第三张表(无m2m字段) 自己链表查询 c. 自定义第三张表(有m2m字段) m=model.ManyToMany('ClassName',through='TableName',through_fields=['id1','id2']) 通过m2m字段查操作 m2m.all() 通过m2m字段 clear ->反向 ->和foreginkey一样 name_set class UserInfo(models.Model): username=models.CharField(max_length=32) email=models.EmailField() usertype=models.ForeignKey(verbose_name='用户类型',to=UserType,to_field='id',on_delete=models.CASCADE) u2g=models.ManyToManyField('UserGroup') class UserGroup(models.Model): groupname=models.CharField(max_length=32) >>> x=models.UserGroup.objects.get(id=3) >>> x.userinfo_set.all() <QuerySet [<UserInfo: UserInfo object (3)>, <UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]> ->操作数据库表 ->基本操作 ->增加(两种) ->create() ->x=models.xxx(y=y1,z=z2) x.save() ->多对多 ->obj.r.add() ->查找 ->all() ->get() ->filter ->exclude ->删除 ->delete ->多对多 ->obj.r.clear() ->obj.r.remove() ->修改 ->update() ->obj.x=xxx obj.save() ->多对多 ->obj.r.set([3,5,7]) ->进阶操作 # 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于 # # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull # Entry.objects.filter(pub_date__isnull=True) # contains # # models.Tb1.objects.filter(name__contains="ven") 相当于like # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似 # # startswith,istartswith, endswith, iendswith, # order by # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc 可以支持多个参数 逐个排序 # group by # # from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset # # models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写 # # Entry.objects.get(title__regex=r'^(An?|The) +') # Entry.objects.get(title__iregex=r'^(an?|the) +') # date # # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year # # Entry.objects.filter(pub_date__year=2005) # Entry.objects.filter(pub_date__year__gte=2005) # month # # Entry.objects.filter(pub_date__month=12) # Entry.objects.filter(pub_date__month__gte=6) # day # # Entry.objects.filter(pub_date__day=3) # Entry.objects.filter(pub_date__day__gte=3) # week_day # # Entry.objects.filter(pub_date__week_day=2) # Entry.objects.filter(pub_date__week_day__gte=2) # hour # # Event.objects.filter(timestamp__hour=23) # Event.objects.filter(time__hour=5) # Event.objects.filter(timestamp__hour__gte=12) # minute # # Event.objects.filter(timestamp__minute=29) # Event.objects.filter(time__minute=46) # Event.objects.filter(timestamp__minute__gte=29) # second # # Event.objects.filter(timestamp__second=31) # Event.objects.filter(time__second=2) # Event.objects.filter(timestamp__second__gte=31) ->queryset所提供的方法 ->all() ->exclude() ->order_by() ->reverse()倒叙,需要和order_by结合使用 ->annotate()分组使用 ->distinct()去重 只能psql用 ->defer 一次sql不取哪几列,如果写这几列,还会发请求取出 ->only 一次sql只取哪几列,如果取其他的列,还会发请求取出 ->extra 了解 mysql的函数 mysql的组合查询 ->两个和性能有关的queryset方法 ->select_related 与之关联的表所有的都拿过来了 ->select_related('foreginkey')只拿这个foreginkey字段 ->prefetch_related 分步查询 ->users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu') -># select * from users where id > 30 # 获取上一步骤中所有的ut_id=[1,2] # select * from user_type where id in [1,2] ->时间的处理 ->date() -> .date() .datetime() def dates(self, field_name, kind, order='ASC'): # 根据时间进行某一部分进行去重查找并截取指定内容 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) # order只能是:"ASC" "DESC" # 并获取转换后的时间 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 # kind只能是 "year", "month", "day", "hour", "minute", "second" # order只能是:"ASC" "DESC" # tzinfo时区对象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """ raw原生sql models.xxx.objects.raw('select * from from tb') ->会转换为queryset 如果表结果不对 models.xxx.objects.raw('select nid as id ,xxname as name from from tb') ->会转换为queryset def none(self): # 空QuerySet对象 get_or_create(username='root',default={'email'='123@123.cn','pwd'=12345}) update_or_create() exists()* ->数据验证(弱) ->obj.full_clean()方法 ->full_clean()如果成功,则继续,如果不成功,则程序报错 ->django预留的save()方法 clean()方法 raise 错误 ->full_clean()每个字段的正则表达式 ->clean()钩子 ->报错没有处理 ->form功能 ->强大的数据验证 流程->前端发来的数据->form挡一层->放入数据库 ->基本功能 ->is_valid()(与models中的full_clean方法对比,full_clean正则表达式,定义clean方法全局二次验证) ->cleaned_data ->生成html as_json as_p as_table ->基本操作 ->单独建立一个forms.py文件 独立开来便于管理 ->类中继承forms.Form ->name=fields.charField() ->生成html 默认input ->widget=widget.Textarea(attrs={})定义插件和属性 ->request=True 是否必填限制 ->max_length=32 长度限制 ->pwd=fields.charField() ->widget=widgets.PasswordInput(attr={'class':'c1'}) ->综上结论 ->字段用来验证数据 ->widget插件用来生成html标签 ->考虑 ->我们是否需要用form的所有功能 ->验证 (一定用)x=LoginForm(request.POST) ->生成html(我们可以自己定义html,并交给form验证) ->新url的方式提交数据 建议使用form来生成html 这样可以达到保留数据的作用 ->如果ajax方式,可以不使用form生成的html,我们可以自己写,但是,同样我们依然能使用form生成 ->step 2 select标签的处理 ->手写 usertype=fields.choicesField(widget=widgets.SELECT,choices=((0,'超管'),(1,'普通用户')) -> choices=models.usertype.objects.value_list('id','name') usertype=fields.choicesField(widget=widgets.SELECT,choices=choices) ->刷新 ->静态字段 ->字段(实例字段) ->实例化变量的过程 ->obj.fields ->静态已经固定 -> choices=[] def __init__ super(UserInfo,self).__init__(*args,**kwargs) #choices self.fields['usertype'].choices=models.usertype.objects.value_list('id','name') #charField self.fields['user_type'].widget.choices=models.usertype.objects.value_list('id','name') 两种写法 ->modlechoiceField与queryset,empty_lable,to_field_name 需要定制str 不推荐 仅做了解 ->step 3 初始化操作 ->传入字典(此处先不考虑获取数据库的情况) 数据验证 ->正则表达式验证(django字典) ->form无法对数据库中是否存在此字段进行验证 ->form自带钩子 is_valid() -> self.errors() -> self.full_clean()(models中也有full_clean)注意区别-> self._clean_fields() self._clean_form() self._post_clean() ->_clean_fields 源码 ->先过正则表达式验证 ->hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value ->clean_name ->return value ->实例 :验证数据库中是否存在 ->错误的raise ->正确的返回值 ->_clean_form 源码 ->self.clean()方法 对整体进行验证 ->返回值 ->抛出异常 __all__的处理 https://www.cnblogs.com/liuzhipenglove/p/8012045.html ->post_clean()源码 ->form自定义正则表达式验证 fields.choicesField(widget=widgets.SELECT,choices=choices,validators=[]) ->执行顺序 ->正则表达式 ->validators ->clean_%s ->clean_form ->post_clean ->成功信息 ->obj.cleaned_data ->错误信息 ->Obj.errors[ '__all__':[] #NONE_CLEANED_FIELDS 'user':[{'code':xxx,'messages':xxx}], 'pwd':[{'code':xxx,'messages':xxx}], ] ->实例 ->ajax请求 进行登录 序列化返回值 obj.errors是一个errordict ,要返回ajax 需要对obj.errors 进行序列化处理 ->思路一 obj.errors.as_json ajax拿到数据需要json.parse两次 ->思路二 as_data validators 怎么处理 封装了 json序列化的过程 json.damp(xxx,cls=yyy) 自定制damp过程 Python 中的isinstance函数,isinstance是Python中的一个内建函数。是用来判断一个对象的变量类型。 ->思路三 ->有道云 ->modelForm 数据库操作+字段验证 fields的正则表达式字段 ->利用models字段来直接使用 ->当model+form的时候 添加(修改)一条数据库内容的操作过程 ->models 定义表 ->form 定义对象 ->form界面生成html ->form验证 ->交给model进行数据库create update的操作 代码 有道云 ->modelform的情况 ->基本定义 class MYModelForm(forms.ModelForm) class Meta: model=models.UserInfo fields='__all__' ->modelform 中文名 verbose_name ->显示部分字段 fields=['f1','f2'] exclude=['f3',f4]#排除谁 ->数据验证 ->原理 Form->BaseForm-> is_valid modelform->Base ModelForm->BaseForm-> is_vaild ->方法 is_valid() ->Meta中的其他参数字段 model fields=None, # 字段 *error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) exclude=None, # 排除字段 *labels=None, # 提示信息 help_text # 帮助提示 *widgets=None # 自定义插件 form中的插件,需要注意此处widgets与插件的widgets重名的情况 *field_classes=None # 自定义字段类 (也可以自定义字段) 将邮箱格式定义为url格式 localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 默认utc时间 ->带*的补充具体代码的字典写法 ->目前生成html已经完成 ->数据验证及保存 obj.is_valid() obj.save() ->直接跳过models的操作 保存了数据 ->一对多 多对多 直接帮我们跳过了所有的关联操作 ->save(false) m2m 的理解 ->编辑实例 ->直接将model对象放入 instance=model.obj ->新增,或更新 instance参数 ->modelform的数据验证 ->和form的钩子完全一样的 ->modelform中定义额外字段的方法 ->可以额外获取值进行操作 ->回顾 ->生成html class Meta ->mf=xxxmodelform(instance=) ->额外标签 ->各种验证 钩子 is_valid() ->保存 save() ->可以拆开 instance.save() ->mf.save_m2m() ->自定制djangoadmin时候 ->ajax ->背景 ->xhr ->xml httprequest object ->ie老版本没有xhr ->jquery 1.x 2.x 3.x ->jquery上层提供封装 ->原生ajax ->从socket角度理解 ->1、建立对象 b = new XMLHttpRequest() ->2、建立连接 open b.open 连接时所需要信息 ->method POST、GET、DELETE... ->url ->async 同步还是异步 ->3、发送内容 send ->4、设置请求头 ->set RequestHeader('key','value') ->5、获取所有响应头的内容 ->getALLResponseHeaders() ->6、获取某一个响应头的内容 ->getResponseHeader('key') ->7、abort()终止请求 ->从ajax角度理解 ->$ajax( {url method data success } ) ->new ->open ->send ->get ->基本操作一 发送请求 b = new xmlHttpRequest() b.open('GET','http://127.0.0.1:8000',true) b.send('test=123') ->有道云截图 ->基本操作二 理解状态码 readState 0-4 0-未初始化,尚未调用open()方法; 1-启动,调用了open()方法,未调用send()方法; 2-发送,已经调用了send()方法,未接收到响应; 3-接收,已经接收到部分响应数据; 4-完成,已经接收到全部响应数据; 理解回调函数onreadystatechange=function(){} 当readState每次发生变化的时候执行此函数 if (b.readState == 4){} 做条件 理解返回值的类型 responseText 主要用Text 字符串数据 包括json数据 responseXML 知道状态码 b.states 知道还原json数据的方法 JSON.parse(b.responseText) 知道如何获取响应头 getResponseHeader 知道如何设置请求头 setRequestHeader('key','value') #csrf 理解post动作的处理 ->设置请求头 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset-UTF-8') 知道简单的兼容性操作 ->y有道代码 知道jquery ajax中获取xhr对象的方法 success参数 ->$.ajax( { success:function(data,x1,,x2) - } ) 伪ajax 理解一:iframe标签的使用 iframe发送请求的方法 ->代码实例 <body> <div>iframe测试</div> <iframe id='my_iframe' src="https://www.baidu.com"></iframe> <div><input id="input_url"></div> <div><input id='iframe_button' type="button" value="提交iframe"></div> <script src="jquery-1.12.4.js"></script> <script> $('#iframe_button').click(function () { var tmp_url = $('#input_url').val(); $('#my_iframe').attr('src',tmp_url); }) </script> </body> ->有道 图片 理解二:一个form表单 如何利用iframe将表单内容提交 form表单 的target参数 target="iframename" iframe放在form表单内 则后台提交 <body> <form action="/p1/xml_ajax" method="post" target="ifm1"> <iframe name="ifm1" id="ifm1"></iframe> <input type="text" name="name"> <input type="text" name="passwd"> <input type="submit"> </form> </body> ->图片 理解三 :获取iframe的返回值 onload事件的理解 对端返回完成后 触发onload ->document对象 ->jquery方式 $('#ifm1').contents().find('body').text(); ->实例 多种ajax方法的选择时机: 普通数据 -> jquery -> xmlHttpRequest -> iframe ->3种ajax上传文件的方法 ->定义一个美观的上传界面的方法 (用一个button来覆盖住input file) <div style="position: relative;height: 60px;width: 100px;" > <input type="file" style="height: 60px;width: 100px; z-index: 90;position: absolute;top:0;bottom: 0;right: 0;left: 0;opacity: 0"> <a style="border-radius: 20px;text-align: center;line-height: 60px;z-index: 50;height: 60px;width: 100px;background-color: deepskyblue;border: 1px solid darkblue;position: absolute;top:0;bottom: 0;right: 0;left: 0">上传</a> </div> ->获取<input type='file'>的文件对象 ->var fobj=document.getElementById('fafafa').files[0] ->原生xhr发送文件 ->依赖FormData对象 ->不需要添加POST头 ->fd_obj.append('key',fobj) ->fd_obj.append('key2','文本') xhrobj.send(fd_obj) -->views ->request.Files.get('key') ->request.POST.get('key2') ->jquery ajax发送文件 ->同样依赖FormDatad对象 $.ajax({ data:f_obj }) ->添加两个参数 processData:false, // tell jquery not to process the data contentType:false, //tell jquery not to set contentTYPE $.ajax({ data:f_obj processData:false, // tell jquery not to process the data contentType:false, //tell jquery not to set contentTYPE }) ->formdata在ie中不兼容 ->iframe 发送文件 form 中 enctype="multipart/form-data 添加<input type='file'>其他和发普通请求完全一致 <body> <form action="/p1/xml_ajax" method="post" target="ifm1" enctype="multipart/form-data> <iframe name="ifm1" id="ifm1"></iframe> <input type="file" name="fafafa"> <input type="submit"> </form> </body> 归纳 文件传输 ->iframe >jquery > 原生ajax ->图片预览 ->图片onload ->iframe submit ->返回值 $('iframename').contents().find('body').text() ->获取图片path ->在相应位置插入图片 var img_obj=document.createElement('img'); img_obj.src=tmp_path; //img_obj.style.width='200px'; //img_obj.style.height='400px'; $('#pre_view').empty().append(img_obj); ->有道云 代码 图片 ->验证码 ->分析 ->不同的人 不同的验证码 ->和session关联 ->生成新的验证码 session更改 ->流程 ->访问界面 ->创建图片并返回用户 ->session存放验证码 ->用户post登录 ->验证post的验证码和session的验证码是否一致 ->图片的url如何处理 预备 ->图片路径 ->界面处理 ->点击刷新 ->访问一个url ->httpresonse返回byte数据,img标签显示图片 实现 pillow模块生成验证码图片句柄f和字符串 生成过程需要依赖一个字体文件 ttf f.save()保存图片 open 文件 httpresponse rb 给前方 如果文件一直被覆盖 如何写入内存BIOSIO 跳过文件保存 字符串写入session中 session和提交的验证码对比 点击刷新(如果url不变浏览器不重新发送请求) ->代码实例 ->kindeditor 阶段一 ->下载kindeditor ->放入static文件夹下 ->定义一个textarea 设置id mykindeditor ->引入jquery ->进入kindeditor中的kindeditor-all.js ->KindEditor.create('#mykindeditor',{}); ->完成 此时界面出现kindeditor框 阶段二 字典定义kindeditor 基本参数 width:'100%',//可以像素,可以百分比 height:'300px',//只能像素 minWidth:'200px',//只能像素 minHeight:'100px'//只能像素 阶段三 一大堆参数 part 1 显示哪些 item:[] //一个数组,显示哪些工具 part 2 可用哪些 noDisableItems:[] designMode 为false时,要保留的工具栏图标。 part 3 xss过滤 filterMode true时根据 htmlTags 过滤HTML代码,false时允许输入任何代码。 数据类型: Boolean 默认值: true ->有道云默认值 ->只是相对 我们在后端还要定义过滤规则 part 4 resizeType 设置kindeditor能否被拖动 3个值 2 1 0 2或1或0,2时可以拖动改变宽度和高度,1时只能改变高度,0时不能拖动。 part 5 禁用鼠标 useContextmenu part 6 syncType 同步数据的方式,可设置”“、”form”,值为form时提交form时自动同步,空时不会自动同步。 part 7 一些参数 ->有道云 复制 part 8 uploadJson 重要 指定上传文件的url post动作 uploadJson:'/p1/kind_upload_pic',上传文件 part 9 上传文件如何处理 ->有道云看代码 ->上传文件的类型如何判断 ?/dir=xxx get动作 part 10 autoHeightMode 自动增高 part 11 csrf处理 part 12 定义发送文件的name filePostName ->分类搜索 step 1 创建数据库内容 ->文章表 ->文章主题表(网站给定不能新增) ->自定义分类表(用户) 界面列出所有内容的标题 models.xxx.objects.all() step 2 后台获取过滤后文章的方法 models.xxx.objects.filter(x_id=1,y_id=2) 字典作为过滤条件 models.xxx.objects.filter(**kwargs) 定义0为all() step 3 url 正则处理 将kwargs处理为x_id y_id 完成后端 构造a标签的url 选定时候的样式添加 全部的处理 ->json与jsonp json是一种数据类型 字符串 jsonp是一种方式 request模块 request发送get response=request.get('https://www.baidu.com') respense.content字节 response.encoding='utf-8' response.text response.cookies response.header requeset发送post 此时浏览器没有给外部发送请求 浏览器给我们的server,我们的server转发请求 通过js发送请求 ->ajax发送请求。 x=requesets.get('http://www.qq.com') >>> print(x.headers) {'Date': 'Thu, 11 Oct 2018 09:34:34 GMT', 'Content-Type': 'text/html; charset=GB2312', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'squid/3.5.24', 'Vary': 'Accept-Encoding, Accept-Encoding, Accept-Encoding', 'Expires': 'Thu, 11 Oct 2018 09:35:35 GMT', 'Cache-Control': 'max-age=60', 'Content-Encoding': 'gzip', 'X-Cache': 'MISS from tianjin.qq.com'} >>> print(x.cookies) <RequestsCookieJar[]> >>> print(x.encoding) GB2312 >>> print(type(x.text)) <class 'str'> >>> print(type(x.content)) <class 'bytes'> >>> --------------------- 使用xhr对象发送get请求 代码及现象 ->浏览器具有同源策略 ->cdn ->创建script标签 添加src 获取内容 ->返回值为ok的情况 ->返回值为alert()的情况 ->返回的数据必须是js格式 ->对端提供功能 返回函数式内容 ->定义js函数 ->服务器接收参数 返回服务端定义的操作 动态函数 ->执行完成后,删除插入的script jsonp就是一种方式 callback jsonp只能发送get请求 不能发送get请求 通过js写 通过jquery写jsonp的请求的实现过程及原理 CORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。 http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301 http://www.weather.com.cn/data/sk/101110101.html >>> import requests >>> requests.get('http://www.weather.com.cn/data/sk/101110101.html') <Response [200]> >>> x=requests.get('http://www.weather.com.cn/data/sk/101110101.html') >>> print(x.text) {"weatherinfo":{"city":"西å®","cityid":"101110101","temp":"23.3","WD":"西åé£","WS":"å°äº3级","SD":"52%","AP":"962.7hPa","njd":"ææ å®åµ","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}} >>> print(x.encoding) ISO-8859-1 >>> x.encoding='utf-8' >>> print(x.text) {"weatherinfo":{"city":"西安","cityid":"101110101","temp":"23.3","WD":"西南风","WS":"小于3级","SD":"52%","AP":"962.7hPa","njd":"暂无实况","WSE":"<3","time":"18:00","sm":"1.2","isRadar":"1","Radar":"JC_RADAR_AZ9290_JB"}} >>>