zoukankan      html  css  js  c++  java
  • Day23-Model操作,Form操作和序列化操作

     

    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 def index(request):
    2     from cmdb.forms import UserInfoForm
    3     obj = UserInfoForm()
    4     return render(request, 'index.html', {'obj':obj})

    forms.py

     1 from django import forms
     2 from django.forms import widgets, fields
     3 from cmdb import models
     4  
     5 class UserInfoForm(forms.Form):
     6     user = fields.CharField(
     7         required=False,
     8         widget=widgets.Textarea(attrs={'class':'c1'})
     9     )
    10     pwd = fields.CharField(
    11         max_length=12,
    12         widget=widgets.PasswordInput(attrs={'class':'c1'})
    13     )
    14     user_type = fields.ChoiceField(
    15         # choices=[(1,'普通用户'),(2,'超级用户')],  # 手动填写
    16         choices=models.UserType.objects.values_list('id','name'),  # 数据库中获取
    17         widget=widgets.Select
    18     )

    models.py

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

    index.html

    1 <body>
    2     <p>{{ obj.user }}</p>
    3     <p>{{ obj.pwd }}</p>
    4     <p>{{ obj.user_type }}</p>
    5     <p>{{ obj.user_type2 }}</p>
    6 </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 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

    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__方法

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

    ModelChoiceField 参数

    1 ModelChoiceField(ChoiceField)
    2     ...                        django.forms.models.ModelChoiceField
    3     queryset,                  # 查询数据库中的数据
    4     empty_label="---------",   # 默认空显示内容
    5     to_field_name=None,        # HTML中value的值对应的字段(html源码中value不同)
    6     limit_choices_to=None      # ModelForm中对queryset二次筛选

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

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

    初始化操作

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

    views.py

    1 def index(request):
    2     from cmdb.forms import UserInfoForm
    3     obj = UserInfoForm({'user':'fgf','user_type':'2'})  # 默认值
    4     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

     1 from django.core.exceptions import ValidationError
     2 class RegisterForm(forms.Form):
     3     user = fields.CharField
     4     email = fields.EmailField
     5  
     6     def clean_user(self):  # 对user做一个单独的验证
     7         c = models.UserType.objects.filter(name=self.cleaned_data['user'])
     8         if c :  # 如果用户名已经存在,提示报错信息
     9             raise ValidationError('用户名已经存在',code='xxx')
    10         else:  # 不存在,验证通过
    11             return self.cleaned_data['user']
    12     def clean_email(self):  # 对email做单独的验证
    13         # 验证必须要有返回值
    14         return self.cleaned_data['email']
    15  
    16 # 验证顺序:
    17     # 先user字段,在clean_user方法
    18     # 再email字段,在clean_email方法

    登录数据验证示例

    forms.py

     1 class LoginForm(forms.Form):
     2     user = fields.CharField
     3     # 这里也可以validators=[],自定义正则数据验证
     4     email = fields.EmailField(validators=[])
     5  
     6     def clean_user(self):
     7         pass
     8     def clean_pwd(self):
     9         pass
    10     # 当用户名和密码验证整体错误,报错给下面的方法
    11     def clean(self):
    12         c = models.User.objects.filter(name=self.cleaned_data.get('user'),pwd=self.cleaned_data.get('pwd'))
    13         if c:
    14             return self.cleaned_data
    15         else:
    16             raise ValidationError("用户名或密码错误")
    17  
    18     # 自定义一些其他验证操作
    19     def _post_clean(self):
    20         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

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

    views.py

    from django import forms
    from django.forms import widgets, fields
    class 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

     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日期,所以可以通过自定义处理器来做扩展,如:

    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 serializers
    
    v = 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 }}

  • 相关阅读:
    Problem D: GJJ的日常之暴富梦(水题)
    Problem I: GJJ的日常之玩游戏(GDC)
    扩展欧几里得,解线性同余方程 逆元 poj1845
    poj3696 欧拉函数引用
    同余
    欧拉函数,打表求欧拉函数poj3090
    洛谷p1072 gcd,质因数分解
    gcd,lcm
    约数 求反素数bzoj1053 bzoj1257
    poj2992 阶乘分解
  • 原文地址:https://www.cnblogs.com/gaodi2345/p/11587341.html
Copyright © 2011-2022 走看看