Django(八)下:Model操作和Form操作、序列化操作

来源:互联网 发布:微信数据如何移到sd卡 编辑:程序博客网 时间:2024/05/21 14:53

二、Form操作

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

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

  • fields :验证(肯定会用的)
  • widgets:生成HTML(有时候用,有时候可以不用)
    • 一般新url方式操作用widgets,因为生成url不是关键的,可以保留上一次提交的数据
    • Ajax请求的时候,可以不用它生成html

1.1、Form操作动态Select数据

urls.py

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

views.py

def index(request):    from cmdb.forms import UserInfoForm    obj = UserInfoForm()    return render(request, 'index.html', {'obj':obj})

forms.py

from django import formsfrom django.forms import widgets, fieldsfrom cmdb import modelsclass 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

class UserType(models.Model):    name = models.CharField(max_length=32)

index.html

<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

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

from django import formsfrom django.forms import widgets, fieldsfrom cmdb import modelsclass 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

from django import formsfrom django.forms import widgets, fieldsfrom django.forms.models import ModelChoiceFieldfrom cmdb import modelsclass 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__方法

class UserType(models.Model):    name = models.CharField(max_length=32)    def __str__(self):        return self.name

ModelChoiceField 参数

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

def index(request):    from cmdb.forms import UserInfoForm    obj = UserInfoForm({'user':'fgf','user_type':'2'})  # 默认值    return render(request, 'index.html', {'obj':obj})

数据验证

views.py

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

from django.core.exceptions import ValidationErrorclass 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

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(伪代码)

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

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

不管提交数据是浏览器提交、还是curl方式,还是ajax提交,只要是request.POST都可以做验证。

Ajax 提交数据,进行验证

urls.py

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

views.py

from django import formsfrom django.forms import widgets, fieldsclass LoginForm(forms.Form):    username = fields.CharField()    password = fields.CharField(        max_length=64,        min_length=12    )def login(request):    import json    res = {'status':True, 'error':None, 'data': None}    if request.method == "GET":        return render(request,"login.html")    elif request.method == "POST":        obj = LoginForm(request.POST)        if obj.is_valid():            print(obj.cleand_data)        else:            # print(obj.errors, type(obj.errors))            res['error'] = obj.errors.as_json()  # 转为json格式            return HttpResponse(json.dumps(res))

login.html

<body>    <form id="fm">        {% csrf_token %}        <p><input type="text" name="username" /></p>        <p><input type="password" name="password" /></p>        <a id="submit">ajax提交</a>    </form>    <script src="/static/jquery-1.12.4.js"></script>    <script>        // 页面框架加载完自动执行        $('#submit').click(function(){            $.ajax({                url:'/login.html',                type:'POST',                data:$('#fm').serialize(),                success:function(arg){                    console.log(arg);                    arg = JSON.parse(arg);  // 转为字典                    console.log(arg);                },                error: function(){                }            })        })    </script></body>

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

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

views.py

from django import formsfrom django.forms import widgets, fieldsclass LoginForm(forms.Form):    username = fields.CharField()    password = fields.CharField(        max_length=64,        min_length=12    )# 序列化,转换为指定数据类型import jsonfrom django.core.exceptions import ValidationErrorclass JsonCustomEncoder(json.JSONEncoder):    def default(self, field):        if isinstance(field, ValidationError):            return {'code':field.code, 'messages': field.messages}        else:            return json.JSONEncoder.default(self, field)def login(request):    res = {'status':True, 'error':None, 'data': None}    if request.method == "GET":        return render(request,"login.html")    elif request.method == "POST":        obj = LoginForm(request.POST)        if obj.is_valid():            print(obj.cleand_data)        else:            # print(obj.errors, type(obj.errors))            # res['error'] = obj.errors.as_json()  # 转为json格式            print(type(obj.errors.as_data()))            for k,v in obj.errors.as_data().items():                print(k,v)  # 这里v是ValidationError类型,不能序列化            res['error'] = obj.errors.as_data()        result = json.dumps(res, cls=JsonCustomEncoder)        return HttpResponse(json.dumps(result))

4、Django 序列化操作

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

ErrorDict自定义JSONEncoder

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

import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder):     def default(self, field):         if isinstance(field, datetime):             return o.strftime('%Y-%m-%d %H:%M:%S')         elif isinstance(field, date):             return o.strftime('%Y-%m-%d')   # 转成字符串类型        else:             return json.JSONEncoder.default(self, field) v = {'k':'123', "k1":datetime.datetime.now()}   ds = json.dumps(v, cls=JsonCustomEncoder) 

QuerySet 第一种序列化方式

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

from django.core import serializersv = models.tb.objects.all()data = serializers.serialize("json", v)

QuerySet 第二种序列化方式

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)  # 把(类似列表的queryset类型)转换为列表v = json.dumps(v,cls=JsonCustomEncoder)  # 这里cls只对ctime操作。# 如果没有ctime这个类型,只写上边的三句就可以实现

注:form里__all__整体的错误信息,前端展示如下:{{ obj.non_field_errors }}


转载请务必保留此出处:http://blog.csdn.net/fgf00/article/details/54629502

0 0
原创粉丝点击