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

    参考源出处:http://blog.csdn.net/fgf00/article/details/54629502

    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

    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 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

    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 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 参数

    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 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

    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 }}

     

     

     

  • 相关阅读:
    Help-IntelliJIDEA-2019.3.4-基础设置:6. 开启自动编译
    Help-IntelliJIDEA-2019.3.4-基础设置:5.Tomcat Server
    Help-IntelliJIDEA-2019.3.4-基础设置:4.自动导包和智能移除 (默认配置)
    Help-IntelliJIDEA-2019.3.4-基础设置:3. 版本控制Git
    Help-IntelliJIDEA-2019.3.4-基础设置:2. 全局Maven(默认配置)
    Help-IntelliJIDEA-2019.3.4-基础设置:1. 全局JDK(默认配置)
    Help-IntelliJIDEA-2019.3.4:IntelliJ IDEA 使用教程
    汉语-词语:笃行
    去除某个元素的属性
    select选中
  • 原文地址:https://www.cnblogs.com/momo8238/p/7904758.html
Copyright © 2011-2022 走看看