Day23-Model操作,Form操作和序列化操作

  • Post author:
  • Post category:其他



1. 搭建环境请参考:http://www.cnblogs.com/momo8238/p/7508677.html


二、Form操作

一般会创建forms.py文件,单独存放form模块。

Form 专门做数据验证,而且非常强大。有以下两个插件:

1-fields :验证(肯定会用的)

2-widgets:生成HTML(有时候用,有时候可以不用)

2.1一般新url方式操作用widgets,因为生成url不是关键的,可以保留上一次提交的数据

2.2Ajax请求的时候,可以不用它生成html


1.1、Form操作动态Select数据



urls.py

1

url(r'^index/$', views.index),


views.py

1
2
3
4

def index(request):



from cmdb.forms import UserInfoForm



obj = UserInfoForm()



return render(request, 'index.html', {'obj':obj})



forms.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

from django import forms

from django.forms import widgets, fields

from cmdb import models

class UserInfoForm(forms.Form):



user = fields.CharField(



required=False,



widget=widgets.Textarea(attrs={'class':'c1'})



)



pwd = fields.CharField(



max_length=12,



widget=widgets.PasswordInput(attrs={'class':'c1'})



)



user_type = fields.ChoiceField(



# choices=[(1,'普通用户'),(2,'超级用户')],  # 手动填写



choices=models.UserType.objects.values_list('id','name'),  # 数据库中获取



widget=widgets.Select



)


models.py

1
2

class UserType(models.Model):



name = models.CharField(max_length=32)



index.html

1
2
3
4
5
6

<


body


>



<


p


>{

{ obj.user }}</


p


>



<


p


>{

{ obj.pwd }}</


p


>



<


p


>{

{ obj.user_type }}</


p


>



<


p


>{

{ obj.user_type2 }}</


p


>

</


body


>

上面有个问题,就是数据库添加数据后,django需要重启网页才能看到新加的数据。why?

forms里面,定义的类,类里的user、pwd、user_type都是静态字段(类变量),这些都是属于类的。



__init__

里面写的属于对象。

而启动Django的时候,类变量一次性都加在到内存了。

上面views.py里的

obj = UserInfoForm()

生成了一个对象,会执行类里的

__init__

方法,会把类里的字段封装到obj.fields里面(里面有user、pwd、user_type三个字段),所以:

views.py

1
2
3
4
5
6
7
8
9

def index(request):



from cmdb.forms import UserInfoForm



from cmdb import models



obj = UserInfoForm()



obj.fields['user_type'].choices = models.UserType.objects.values_list('id','name')



return render(request, 'index.html', {'obj':obj})

这样,数据库添加数据,网页上就可以实时更新了。但是如果有很多choices的话,也会写很多,也不好。

所以不在views.py里做修改,可以在forms.py里类的

__init__

构造方法里写


forms.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

from django import forms

from django.forms import widgets, fields

from cmdb import models

class UserInfoForm(forms.Form):



user = fields.CharField(



required=False,



widget=widgets.Textarea(attrs={'class':'c1'})



)



pwd = fields.CharField(



max_length=12,



widget=widgets.PasswordInput(attrs={'class':'c1'})



)



user_type = fields.ChoiceField(



# choices=[(1,'普通用户'),(2,'超级用户')],  # 手动填写



choices=[],  # 构造方法里取,这里只定义就可以了,不需要在获取一次。



widget=widgets.Select



)



#### 另外一种写法 方式 ####



user_type2 = fields.ChoiceField(



widget=widgets.Select(choices=[])



)



def __init__(self, *args, **kwargs):



super(UserInfoForm,self).__init__(*args, **kwargs)



self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')



#### 另外一种写法 方式 ####



self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')


1.2、Form操作动态Select数据

上面1.1里是自己获取实现的,Django也可以自动实现,只是实现的不太好。

django 里自动实现的,看user_type3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

from django import forms

from django.forms import widgets, fields

from django.forms.models import ModelChoiceField

from cmdb import models

class UserInfoForm(forms.Form):



user = fields.CharField(



required=False,



widget=widgets.Textarea(attrs={'class':'c1'})



)



pwd = fields.CharField(



max_length=12,



widget=widgets.PasswordInput(attrs={'class':'c1'})



)



user_type = fields.ChoiceField(



choices=[],



widget=widgets.Select



)



user_type2 = fields.ChoiceField(



widget=widgets.Select(choices=[])



)



user_type3 = ModelChoiceField(



queryset=models.UserType.objects.all()



)



def __init__(self, *args, **kwargs):



super(UserInfoForm,self).__init__(*args, **kwargs)



self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')



self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')


使用Django自动提供的这个方法,有一点不好的就是:需要自己在models里加上

__str__


方法

1
2
3
4
5

class UserType(models.Model):



name = models.CharField(max_length=32)



def __str__(self):



return self.name

ModelChoiceField 参数

1
2
3
4
5
6

ModelChoiceField(ChoiceField)



...                        django.forms.models.ModelChoiceField



queryset,                  # 查询数据库中的数据



empty_label="---------",   # 默认空显示内容



to_field_name=None,        # HTML中value的值对应的字段(html源码中value不同)



limit_choices_to=None      # ModelForm中对queryset二次筛选

ModelChoiceField 是单选的,多选的是:ModelMultipleChoiceField




2、Form内置钩子(数据验证)



初始化操作

加一个字典,生成显示默认值

views.py

1
2
3
4

def index(request):



from cmdb.forms import UserInfoForm



obj = UserInfoForm({'user':'fgf','user_type':'2'})  # 默认值



return render(request, 'index.html', {'obj':obj})

数据验证

views.py

1
2
3
4
5
6
7
8
9
10

def index(request):



from cmdb.forms import UserInfoForm



if request.method == 'GET':



obj = UserInfoForm({'user':'fgf','user_type':'2'})



return render(request, 'index.html', {'obj':obj})



elif request.method == 'POST':



obj = UserInfoForm(request.POST, request.FILES)  # request.FILES:接收文件



obj.is_valid()  # 数据验证

正则表达式验证成功了,但是如果用户名重复了,也是不让执行的,如何自定制呢?



注册数据验证示例

forms.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

from django.core.exceptions import ValidationError

class RegisterForm(forms.Form):



user = fields.CharField



email = fields.EmailField



def clean_user(self):  # 对user做一个单独的验证



c = models.UserType.objects.filter(name=self.cleaned_data['user'])



if c :  # 如果用户名已经存在,提示报错信息



raise ValidationError('用户名已经存在',code='xxx')



else:  # 不存在,验证通过



return self.cleaned_data['user']



def clean_email(self):  # 对email做单独的验证



# 验证必须要有返回值



return self.cleaned_data['email']

# 验证顺序:



# 先user字段,在clean_user方法



# 再email字段,在clean_email方法

登录数据验证示例

forms.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class LoginForm(forms.Form):



user = fields.CharField



# 这里也可以validators=[],自定义正则数据验证



email = fields.EmailField(validators=[])



def clean_user(self):



pass



def clean_pwd(self):



pass



# 当用户名和密码验证整体错误,报错给下面的方法



def clean(self):



c = models.User.objects.filter(name=self.cleaned_data.get('user'),pwd=self.cleaned_data.get('pwd'))



if c:



return self.cleaned_data



else:



raise ValidationError("用户名或密码错误")



# 自定义一些其他验证操作



def _post_clean(self):



pass

验证顺序

这么多钩子,这么强大的数据验证,验证顺序是:

form循环,

  • 先第一个字段正则表达式判断,执行字段钩子函数;
  • 第二个字段正则,第二个的钩子;
  • 所有字段完成后,执行clean钩子;
  • clean执行完后,执行

    _post_clean

    钩子


怎么去记:

通过看源码去找:

先找

is_valid

,找到

errors

,找到

full_clean

,所有的钩子基于

full_clean

执行的



验证完成后

views.py(伪代码)

 1 def register(request):
 2     from cmdb.forms import RegisterForm
 3     from django.core.exceptions import NON_FIELD_ERRORS
 4     obj = RegisterForm(request.POST)
 5     if obj.is_valid():
 6         obj.cleand_data
 7 
 8     else:
 9         obj.errors
10         {
11             'user':[{'code':'required','message':'xxxx'}],
12             'pwd':[{'code':'required','message':'xxxx'}],
13             # 上面每个字段的错误信息放在里面,那clean总的错误信息放在哪里?
14             '__all__':[],  # 特殊的整体错误信息,放在这里
15             # NON_FIELD_ERRORS:[], 和 __all__ 一个意思。
16         }


3、Form内置序列化错误信息

不管提交数据是浏览器提交、还是curl方式,还是ajax提交,只要是

request.POST

都可以做验证。



Ajax 提交数据,进行验证

urls.py

1 url(r'^login.html$', views.login),


views.py

 1 from django import forms
 2 from django.forms import widgets, fields
 3 class LoginForm(forms.Form):
 4     username = fields.CharField()
 5     password = fields.CharField(
 6         max_length=64,
 7         min_length=12
 8     )
 9 
10 def login(request):
11     import json
12     res = {'status':True, 'error':None, 'data': None}
13     if request.method == "GET":
14         return render(request,"login.html")
15     elif request.method == "POST":
16         obj = LoginForm(request.POST)
17         if obj.is_valid():
18             print(obj.cleand_data)
19         else:
20             # print(obj.errors, type(obj.errors))
21             res['error'] = obj.errors.as_json()  # 转为json格式
22             return HttpResponse(json.dumps(res))



login.html

 1 <body>
 2     <form id="fm">
 3         {% csrf_token %}
 4         <p><input type="text" name="username" /></p>
 5         <p><input type="password" name="password" /></p>
 6         <a id="submit">ajax提交</a>
 7     </form>
 8     <script src="/static/jquery-1.12.4.js"></script>
 9     <script>
10         // 页面框架加载完自动执行
11         $('#submit').click(function(){
12             $.ajax({
13                 url:'/login.html',
14                 type:'POST',
15                 data:$('#fm').serialize(),
16                 success:function(arg){
17                     console.log(arg);
18                     arg = JSON.parse(arg);  // 转为字典
19                     console.log(arg);
20                 },
21                 error: function(){
22 
23                 }
24             })
25         })
26     </script>
27 </body>

上面的方式可以实现,只是前端需要反解两次,不太好,下面优化一下。

  • as_json : 生成json格式
  • as_data : 生成dict数据。


views.py

 1 from django import forms
 2 from django.forms import widgets, fields
 3 class LoginForm(forms.Form):
 4     username = fields.CharField()
 5     password = fields.CharField(
 6         max_length=64,
 7         min_length=12
 8     )
 9 
10 # 序列化,转换为指定数据类型
11 import json
12 from django.core.exceptions import ValidationError
13 class JsonCustomEncoder(json.JSONEncoder):
14     def default(self, field):
15         if isinstance(field, ValidationError):
16             return {'code':field.code, 'messages': field.messages}
17         else:
18             return json.JSONEncoder.default(self, field)
19 
20 def login(request):
21     res = {'status':True, 'error':None, 'data': None}
22     if request.method == "GET":
23         return render(request,"login.html")
24     elif request.method == "POST":
25         obj = LoginForm(request.POST)
26         if obj.is_valid():
27             print(obj.cleand_data)
28         else:
29             # print(obj.errors, type(obj.errors))
30             # res['error'] = obj.errors.as_json()  # 转为json格式
31             print(type(obj.errors.as_data()))
32             for k,v in obj.errors.as_data().items():
33                 print(k,v)  # 这里v是ValidationError类型,不能序列化
34             res['error'] = obj.errors.as_data()
35         result = json.dumps(res, cls=JsonCustomEncoder)
36         return HttpResponse(json.dumps(result))


4、Django 序列化操作


Json.dumps

用来做序列化,但是只能序列化一些简单的基本数据类型。对于无法序列化的数据类型,只能自定制了。



ErrorDict自定义JSONEncoder

由于json.dumps时无法处理datetime日期,所以可以通过自定义处理器来做扩展,如:

 1 import json 
 2 from datetime import date 
 3 from datetime import datetime 
 4 
 5 class JsonCustomEncoder(json.JSONEncoder): 
 6 
 7     def default(self, field): 
 8 
 9         if isinstance(field, datetime): 
10             return o.strftime('%Y-%m-%d %H:%M:%S') 
11         elif isinstance(field, date): 
12             return o.strftime('%Y-%m-%d')   # 转成字符串类型
13         else: 
14             return json.JSONEncoder.default(self, field) 
15 
16 v = {'k':'123', "k1":datetime.datetime.now()}   
17 ds = json.dumps(v, cls=JsonCustomEncoder) 

QuerySet 第一种序列化方式

上面自己写实现也可以,不过Django提供了方法做序列化

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

QuerySet 第二种序列化方式

 1 import json 
 2 from datetime import date 
 3 from datetime import datetime 
 4 
 5 class JsonCustomEncoder(json.JSONEncoder): 
 6 
 7     def default(self, field): 
 8 
 9         if isinstance(field, datetime): 
10             return field.strftime('%Y-%m-%d %H:%M:%S') 
11         elif isinstance(field, date): 
12             return field.strftime('%Y-%m-%d') 
13         else: 
14             return json.JSONEncoder.default(self, field) 
15 
16 v = models.tb.objects.values('id','name','ctime')
17 v = list(v)  # 把(类似列表的queryset类型)转换为列表
18 v = json.dumps(v,cls=JsonCustomEncoder)  # 这里cls只对ctime操作。
19 # 如果没有ctime这个类型,只写上边的三句就可以实现


注:form里

__all__


整体的错误信息,前端展示如下:

{

{ obj.non_field_errors }}


转载于:https://www.cnblogs.com/gaodi2345/p/11587341.html