zoukankan      html  css  js  c++  java
  • Django组件--forms组件(注册用)

     
    from django.shortcuts import render,HttpResponse
    from app01 import models
    
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets#插件 as 设置别名
    
    class UserInfoModelForm(forms.ModelForm):
        #自定义字段可以在前端展示 ,不与数据库关联
        is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
        class Meta:
            model = models.UserInfo#指定到一个类(表)中的字段
            fields = '__all__'#所有字段 (列)
            # fields =  ['username','email']#可以指定的列
            # exclude = ['username']#排除指定项
            #定义标签
            labels = {
                'username': '用户名',
                'email': '邮箱',
            }
            help_texts = {#帮助提示信息
                'username': '...'
            }
            widgets = {#自定义插件 使用 form的插件
                'username': Fwidgets.Textarea(attrs={'class': 'c1'})
            }
            error_messages = {
                '__all__':{
                    #整体的错误信息
                },
                'email': {#指定的错误信息
                    'required': '邮箱不能为空',
                    'invalid': '邮箱格式错误..',
                }
            }
            field_classes = {#字段的类
                # 'email': Ffields.URLField
            }
    
            # localized_fields=('ctime',)
    
        def clean_username(self):
            old = self.cleaned_data['username']
            return old
    
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email = Ffields.EmailField()
        user_type = Ffields.ChoiceField(
            #从数据库取值
            choices=models.UserType.objects.values_list('id','caption')#取选 项列表
        )
        #重写父类
        def __init__(self, *args, **kwargs):
            super(UserInfoForm,self).__init__(*args, **kwargs)
            self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')#更新数据
    
    
    def index(request):
        if request.method == "GET":
            obj = UserInfoModelForm()
            return render(request,'index.html',{'obj': obj})
        elif request.method == "POST":
            obj = UserInfoModelForm(request.POST)
            if obj.is_valid():
                # obj.save()#完成所有对应关系的保存
                instance = obj.save(False)#手动对应关系进行保存
                instance.save()#只保存表的数据
                obj.save_m2m()#保存对应表的关系的表
    
    
            # print(obj.is_valid())
            # print(obj.cleaned_data)
            # print(obj.errors.as_json())
            return render(request,'index.html',{'obj': obj})
    
    #用户列表
    def user_list(request):#    所有       跨表条件
        li = models.UserInfo.objects.all().select_related('user_type')
        return render(request,'user_list.html',{'li': li})
    #用户信息编辑
    def user_edit(request, nid):
        # 获取当前id对象的用户信息
        # 显示用户已经存在数据
        if request.method == "GET":
            user_obj = models.UserInfo.objects.filter(id=nid).first()
            #调用ModelForm        默认值
            mf = UserInfoModelForm(instance=user_obj)
            return render(request,'user_edit.html',{'mf': mf, 'nid': nid})
        elif request.method == 'POST':
            user_obj = models.UserInfo.objects.filter(id=nid).first()
            mf = UserInfoModelForm(request.POST,instance=user_obj)
            if mf.is_valid():
    
                mf.save()
            else:
                print(mf.errors.as_json())
            return render(request,'user_edit.html',{'mf': mf, 'nid': nid})
    ModelForm

     limit_choice_to

    from django.db import models
    from rbac.models import UserInfo as RbacUserInfo
    
    
    class School(models.Model):
        '''
        校区表
        '''
        title = models.CharField(verbose_name='校区名称', max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class Department(models.Model):
        '''
        课程表
        '''
        title = models.CharField(verbose_name='部门名称', max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class UserInfo(RbacUserInfo):
        '''
        用户表
        '''
        nickname = models.CharField(verbose_name='姓名', max_length=32)
        gender_choices = (
            (1, ''),
            (2, ''),)
        gender = models.IntegerField(verbose_name='性别', choices=gender_choices)
        phone = models.CharField(verbose_name='手机号', max_length=32)
        email = models.EmailField(verbose_name='邮箱', )
        depart = models.ForeignKey(verbose_name='所属部门', to='Department', on_delete=models.CASCADE)
    
        def __str__(self):
            return self.nickname
    
    
    class Course(models.Model):
        '''
        课程表
        '''
        name = models.CharField(verbose_name='课程名称', max_length=32)
    
        def __str__(self):
            return self.name
    
    
    class ClassTb(models.Model):
        '''
        班级表
        '''
        school = models.ForeignKey(verbose_name='所属校区', to='School', on_delete=models.CASCADE)
        course = models.ForeignKey(verbose_name='课程', to='Course', on_delete=models.CASCADE)
        semester = models.PositiveIntegerField(verbose_name='班级(期)')
        price = models.PositiveIntegerField(verbose_name='学费')
        start_date = models.DateField(verbose_name='开班日期')
        graduate_date = models.DateField(verbose_name='结业日期', null=True, blank=True)
        manage_teacher = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',
                                           on_delete=models.CASCADE, limit_choices_to={'depart__title': '教质部'})
        tech_teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='tech_classes', blank=True,
                                               limit_choices_to={'depart_id__lt': 4})
        memo = models.TextField(verbose_name='说明', null=True, blank=True)
    
        def __str__(self):
            return '%s-%d期' % (self.course, self.semester)
    limit_choice_to

     保存前修改form的值:

    form=MyModelForm(..)

     form.instance.depart__id=2

    form.save()

    from django.forms.fields import DateField, TimeField, DateTimeField
    class BootstrapForm(forms.ModelForm):
        '''
        作用是给每个widget添加class:form-control样式
        '''
    
        def __init__(self, *args, **kwargs):
            super(BootstrapForm, self).__init__(*args, **kwargs)
            for name, field in self.fields.items():
                if isinstance(field, DateField):
                    field.widget.input_type = 'date'
                elif isinstance(field, DateTimeField):
                    field.widget.input_type = 'datetime-local'
                elif isinstance(field, TimeField):
                    field.widget.input_type = 'time'
                # input_type = field.widget.input_type
                # if input_type in ['text', 'email', 'number', 'password', 'url', 'select']:
                #     field.widget.attrs['class'] = 'form-control'
                if 'password' in name or 'pwd' in name:
                    field.widget.input_type = 'password'
                    field.widget.input_type = 'password'
                field.widget.attrs['class'] = 'form-control'

    一、forms组件--校验类的使用

    二、form组件--校验类的参数

    三、forms组件校验的局部钩子--自定义校验规则(要看源码理解)

    四、forms组件校验的全局钩子--校验form表单两次密码输入是否一致

    五、forms组件的渲染标签功能(推荐方式二)

    六、formset 批量增加

    6.1 初级讲解示例(form基本验证,唯一约束验证)

    6.2 写在前面,如何进行重复字段验证?

    6.3 自我提高(form基本验证,唯一约束验证,formset重复项验证)(方式一:还行,结构不太美观,展示不错,性能可以)

    6.4 自我提高(form基本验证,唯一约束验证,formset重复项验证)(方式二:终极版,结构清晰,展示不错,性能可以)

    七、formset 批量修改(带唯一验证)

    0、基本用法:

    forms.CharField(max_length=30) # 文本
    forms.EmailField() # 邮箱
    forms.IntegerField() # 验证是否整数
    forms.FloatField() # 浮点数验证
    forms.ChoiceField(choices=((1, 'a'), ),) # 单选下拉框
    forms.MultipleChoiceField(choices=((1, 'a'), (2, 'b')), ) # 多选项列表框
    forms.DateField() # 日期类型
    forms.DateTimeField() # 日期时间类型
    forms.TimeField() # 验证是否为时间格式
    forms.RegexField(regex=r'^d+$') # 验证是否符合正则表达式
    forms.FileField() # 验证是否为文件
    forms.ImageField() # 验证是否图片文件
    forms.BooleanField() # checkbox框
    forms.DecimalField() # 精确浮点数?
    forms.DurationField() # 时长验证
    forms.FilePathField(path='c:\') # c:\路径下的文件下拉框
    forms.GenericIPAddressField() # ipv4或ipv6地址验证
    forms.NullBooleanField() # 单选下拉框,三个选项unknow/yes/no
    forms.SlugField() # 验证是否连续的字母、数字、下划线或横线组成。
    forms.URLField() # 验证是否为URL格式
    forms.UUIDField() # 验证是否为UUID格式
    =========
    from django import forms
    class FormControlForm(forms.Form):
    '''
    作用是给每个widget添加class:form-control样式
    '''
    def __init__(self, *args, **kwargs):
    super(FormControlForm, self).__init__(*args, **kwargs)
    for name, field in self.fields.items():
    input_type = field.widget.input_type
    if input_type in ['text', 'email', 'number', 'password', 'url', 'select']:
    field.widget.attrs['class'] = 'form-control'
    ===========
    forms框架把每一个字段的显示逻辑分离到一组部件(widget)中。 
    每一个字段类型都拥有一个默认的部件,我们也可以容易地替换掉默认的部件,或者提供一个自定义的部件。
    Field类表现校验逻辑,而widget表现显示逻辑。

    模板渲染:{{form.as_ul() }} 或者{{form.as_p()}} 或者{{ form }}
    查看出错信息:
    >>> f['message'].errors
    >>> f['subject'].errors
    >>> f['email'].errors
    渲染错误信息
    {{ field.errors.0 }}
    如果要加默认参数:示例
    ContactForm(initial={'email':'122121@qq.com'})
    class ContactForm(forms.Form):
    # required: 是否必填
    # max_length: 最大长度
    # initial: 渲染时的初始值
    # attrs: 添加class样式
    # label: 自定义的input框名称
    # error_messages: 自定义错误输出语句
    subject = forms.CharField(required=False, max_length=20, initial='abc',
    label="用户名", error_messages={"required": "不能为空", "invalid": "格式错误"})
    email = forms.EmailField(required=False)
    message = forms.MultipleChoiceField(choices=((1, 'a'), (2, 'b'), (3, 'c')),
    widget=forms.SelectMultiple(attrs={'class': 'ass'}))
      # 如果是date类型:
      pay_time = forms.DateField(input_formats='%d/%m/%Y', required=False,

    widget=forms.DateInput(attrs={'type': 'date', "class": "form-control"},
    format='%d/%m/%Y'))
        '''
    message多重选择框渲染效果:
    <select name="message" class="ass" required="" id="id_message" multiple="">
    <option value="1">a</option>
    <option value="2">b</option>
    <option value="3">c</option>
    </select>
    '''
    # form = ContactForm({'subject': '321', 'email': '2332@qq.com', 'message': [1, 3]})
    # form.is_valid() 等于True,通过校验

     ==============================

    # 多选框的渲染,用内置方法获取数据库数据:方式一
    from django import forms
    class FInfo(forms.Form):
        authors = forms.ModelMultipleChoiceField(queryset=models.Menu.objects.all())
    #如果用这种方式渲染,直接写all(), 渲染的多选框的value是Menu表的主键,值是Menu的__str__()方法返回的值
    #如果用这种方式渲染,forms里的外键一定要写对象名,而且初始化值的时候也要用对象,因为提交回来的是对象,必须用对象名来接收。

    ==============================
    class MyForm(Form):
      
        user = fields.ChoiceField(
            # choices=((1, '上海'), (2, '北京'),),
            initial=2,
            widget=widgets.Select
        )
       # 单选框的渲染,自己写init函数获取数据:方式二
        def __init__(self, *args, **kwargs):
            super(MyForm,self).__init__(*args, **kwargs)
            # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
            # 或
            self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
    #如果用这种方式渲染,forms里的外键要写对象ID,而且初始化值的时候也要用对象ID,因为提交回来的是对象ID,必须用对象ID来接收。
    =========================================
    def add(request):
    if request.method == 'POST':
    print(request.POST)
    pf = PaymentForm()
    pf.fields['pay_time'].disabled = True
    return render(request, "web-add-edit.html", {"form": pf})
    ==============================
    # 密码输入框的处理:解决用自带的PasswordInput“密码输入验证失败,返回表单无密码”问题
    class UserForm(forms.Form):
    name = forms.CharField(label='用户名', max_length=32, widget=forms.TextInput(attrs={"class": "form-control"}))
    email = forms.EmailField(label='邮箱', max_length=32, widget=forms.EmailInput(attrs={"class": "form-control"}))
    password = forms.CharField(label='密码', max_length=64, widget=forms.TextInput(attrs={"class": "form-control"}))
    re_password = forms.CharField(label='重复密码', max_length=64, widget=forms.TextInput(attrs={"class": "form-control"}))
    def __init__(self, *args, **kwargs):
    super(UserForm, self).__init__(*args, **kwargs)
    self.fields['password'].widget.input_type = 'password'
    self.fields['re_password'].widget.input_type = 'password'
    ======================

    一、forms组件--校验类的使用

    1、校验字段功能(最核心功能)

    示例1:
    在服务端进行格式校验
    -------这个写在一个新模块里-------------
    from django import forms
    class UserForm(forms.Form): #新建一个校验类
      name = forms.CharField(min_length) #必须为字符串,最小4位
      email = forms.EmailField() #邮箱校验

    --------------------------------------------------

    -------视图函数里的用户注册方法-------

    def register(request):
        # 字典的键必须跟校验类里的字段一样
      form=UserForm({"name":"yuan","email":"123"})
        # 开始校验,所有校验类里的值校验通过返回True,否则返回False
        # 字典里有多少个无所谓,只要校验类里的字段通过就是True
      bool = form.is_valid()
        # cleaned_data 这里面放所有校验通过的键值,注意:必须在调用is_valid()方法之后才有cleaned_data
      form.cleaned_data
        # errors 这里面放所有未通过的键值 是仿字典类型
        # 例如name没通过,要取errors里的错误信息:form.errors["name"][0]
      form.errors

      return HttpResponse(request, "ok")

    ------------------------------------------------------

    完整示例:

    def register(request):
        #校验类字段要跟前端form表单一样
      form=UserForm(request.POST)
      if form.is_valid():
        clean = form.cleaned_data
      else:
        clean = form.cleaned_data
        errors = form.errors
    -------------------------------------------------

    完整示例一:注册页面。ajax提交post数据,返回json,局部刷新

    def register(request):
      if request.method=="POST":
        form = UserForm(request.POST)
        response = {"user":None,"msg":None}
        if form.is_valid():
          user = form.cleaned_data.get("user")
          pwd = form.cleaned_data.get("pwd")
          email = form.cleaned_data.get("email")
          # 文件对象或者图片对象都按这样处理
          avatar_obj = request.FILES.get("avatar")
          extra_fields = {}
          if avatar_obj:
          extra_fields["avatar"] = avatar_obj
          UserInfo.objects.create_user(username=user,password=pwd,email=email,**extra_fields)

          response["user"] = user
          response["msg"] = "ok"
        else:
          response["msg"] = form.errors
          return JsonResponse(response)
      form = UserForm()
      return render(request,"register.html",{"form":form})

    -----------------------------------

    //提交注册
        $("#reg-btn").click(function () {
            //formdata是上传文件专用
            var formdata = new FormData();
            var request_data = $("#form").serializeArray();
            $.each(request_data,function (index, data) {
                formdata.append(data.name,data.value)
            });
            formdata.append("avatar",$("#avatar")[0].files[0]);
    
            $.ajax({
               url:'',
               type:'post',
                contentType:false,
                processData:false,
               data:formdata,
                success:function (data) {
                    if(data.user){
                        //校验成功
                        location.href="/login/";
                    }else{
                        //校验失败
                        //先清空
                        $("span.error").text("");
                        $("span.error").parent().removeClass("has-error");
                        //再加上错误信息
                        $.each(data.msg,function (name, error_list) {
                            if(name=="__all__"){
                                $("#id_re_pwd").next().text(error_list[0]);
                                $("#id_re_pwd").parent().addClass("has-error");
                            }
                            $("#id_"+name).next().text(error_list[0]);
                            $("#id_"+name).parent().addClass("has-error");
                        });
                    }
                }
            });
        });    

    ----------

    完整示例二:表单提交post请求,全局刷新,修改和新增共享同一个模板

    def add(request):
    '''
    新增客户
    :param request:
    :return:
    '''
    cf = None
    if request.method == 'POST':
    cf = CustomerForm(request.POST)
    if cf.is_valid():
    # 通过校验:写入数据库,并重定向
    clean = cf.cleaned_data
    models.Customer.objects.create(name=clean.get('name'),
    age=clean.get('age'),
    email=clean.get('email'),
    company=clean.get('company'))
    return redirect(reverse('customer_list'))
    if request.method == 'GET':
    cf = CustomerForm(initial={})
    # 1.GET请求:返回空的form渲染页面
    # 2.未通过校验:form里包括用户上传的表单数据,和表单数据的错误信息
    return render(request, "web-customer-add.html", {'form': cf})
    -----------------
    def edit(request, id):
    '''
    编辑客户
    :param request:
    :param id:
    :return:
    '''
    obj_query_set = models.Customer.objects.filter(id=id)
    obj = obj_query_set.first()
    # 如果找不到该对象,返回404页面
    if not obj:
    return render(request, "web-404.html")
    # 能找到该对象,判断请求方式
    cf = None
    if request.method == 'POST':
    cf = CustomerForm(request.POST)
    if cf.is_valid():
    # 通过校验:更新记录,并重定向
    clean = cf.cleaned_data
    obj_query_set.update(name=clean.get('name'),
    age=clean.get('age'),
    email=clean.get('email'),
    company=clean.get('company'))
    return redirect(reverse('customer_list'))
    if request.method == 'GET':
    obj_dict ={'pay': obj.pay, 'pay_time': obj.pay_time, 'customer': obj.customer.id}
            cf = CustomerForm(initial = obj_dict)
    # 1.GET请求:用查询到的obj字典渲染页面
    # 2.未通过校验:form里包括用户上传的表单数据,和表单数据的错误信息
    return render(request, "web-customer-add.html", {'form': cf})

    --------------------------------------------------

                <form method="post">
                      {% csrf_token %}
                      {% for field in form %}
                    <div class="form-group">
                        <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }}
                        <span class="err">{{ field.errors.0 }}</span>
                    </div>
                      {% endfor %}
                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>

    -------------------------------------------------

    总结:
    1.注意前端form表单内的需要校验的属性名称,要跟后端校验类的字段一致
    2. 掌握is_valid() 、 cleaned_data、 errors各自含义

    二、form组件--校验类的参数

    如果想改渲染的标签类型为password:widgets.PasswordInput
    如果想改错误提示信息:error_messages参数。提示信息支持中文:settings.py文件里-》LANGUAGE_CODE='zh-hans'
    如果需要给渲染的标签加样式类, 就要加属性键值对,什么都行:widget=widgets.TextInput(attrs={"class":"form-control"})

    from django.forms import widgets
    class UserForm(forms.Form): 
      name = forms.CharField(min_length=4, label="用户名", error_messages={"required":"不能为空","invalid":"格式错误"})  #错误类型不能改其他名字
      pwd = forms.CharField(min_length=4, label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"})) 
      r_pwd = forms.CharField(min_length=4, label="确认密码") 
      email = forms.EmailField(label="邮箱",widget=widgets.TextInput(attrs={"class":"form-control"}))  # 给标签加属性
      tel = forms.CharField(label="电话")

    三、forms组件校验的局部钩子--自定义校验规则(要看源码理解)

    第二层校验:clean_xx方法,校验通过返回该值,否则抛ValidationError异常

    ------------------------------------------------------------------------------------

    from django.core.exceptions import ValidationError
    class UserForm(forms.Form): 
      name = forms.CharField(min_length=4, label="用户名")
      pwd = forms.CharField(min_length=4, label="密码") 
      r_pwd = forms.CharField(min_length=4, label="确认密码") 
      email = forms.EmailField(label="邮箱")
      tel = forms.CharField(label="电话")

      #该功能验证用户名是否已被注册
      def clean_name(self):
        name = self.cleaned_data.get("name")
        ret = UserInfo.objects.filter(name=name)
        if not ret:
          return name
        else:
          #只能抛这个错误类型
          raise ValidationError("该用户已注册")
    ----------------------------------------------------------------------------------------

    四、forms组件校验的全局钩子--校验form表单两次密码输入是否一致

    在校验类里,覆盖父类clean方法

    获取全局钩子错误(在模板里渲染):form.errors.get("__all__")[0]

    def clean():
      # 先从cleaned_data中取要联合校验的字段
      pwd=self.cleaned_data.get('pwd')
      r_pwd=self.cleaned_data.get('r_pwd')
      if pwd and r_pwd:
        # pwd跟r_pwd之前的校验已通过,才验证全局钩子
        if pwd==r_pwd:
            # 这里面包括pwd和r_pwd
          return self.cleaned_data
        else:
          raise ValidationError('两次输入密码不一致')
      else:
          # 这里面包括pwd和r_pwd两者之一,或者都不包括
        return self.cleaned_data

    五、forms组件的渲染标签功能(推荐方式二)

    渲染方式三种、错误信息的渲染方式

    1、渲染方式一
    先写后端校验类,再在前端渲染,charFiled渲染成input标签:

    # 校验类
    class UserForm(forms.Form):
      name = forms.CharField(min_length=4)
      pwd = forms.CharField(min_length=4)
      r_pwd = forms.CharField(min_length=4)
      email = forms.EmailField()
      tel = forms.CharField()

    # 视图函数:用户注册
    def reg(request):
      form = UserForm(request.POST)
      return render("reg_html",{"form":form})

    为了方便,Django可以在前端按照后端写的校验类进行渲染
    <form>
      {% csrf_token %}
      用户名:{{ form.name }}
      密码:{{ form.pwd }}
      确认密码:{{ form.r_pwd }}
      邮箱:{{ form.email }}
      电话:{{ form.tel }}
      <input type="submit">
    </form>

    2、渲染方式二:如果字段很多
    class UserForm(forms.Form):
      name = forms.CharField(min_length=4, label="用户名")
      pwd = forms.CharField(min_length=4, label="密码")
      r_pwd = forms.CharField(min_length=4, label="确认密码")
      email = forms.EmailField(label="邮箱")
      tel = forms.CharField(label="电话")

    def reg(request):
      form = UserForm(request.POST)
      return render("reg_html",{"form":form})
    -------------------------------
    <form method="post">
      {% csrf_token %}
      {% for field in form %}
        <div>
        <label for=" {{ field.auto_id }}">{{ field.label }}</label>  {{ field }}
        </div>
      {% endfor %}
      <input type="submit">
    </form>

    如果method不写会以get方式提交参数。写了post才以post方式提交

    -------------------------------
    3、渲染方式三,自己搞着玩可以
    <form>
      {% csrf_token %}
      {{ form.as_p }}
      <input type="submit">
    </form>


    4、渲染错误信息

    -------视图函数------
    def register(request):
      if request.method=="POST":
        form=UserForm(request.POST)
        if form.is_valid():
          clean = form.cleaned_data
        else:
          clean = form.cleaned_data
          errors = form.errors
          # NOTES:这里返回的页面可以保存用户上次已经填过的数据,还包括错误信息
        return render("reg_html",{"form":form})

      #如果是get请求,渲染空form页面
      form=UserForm()
      return render(request,"reg_html",{"form":form})
    ------------------------------

    <form>
      {% csrf_token %}
      {% for field in form %}
        <div>    
          <label for="">{{ field.label }}</label>
           {{ field }}
          <span>{{ field.errors.0 }}</span>
        </div>
      {% endfor %}
      <input type="submit">
    </form>

    --------------------------------

     六、formset 批量增加

    6.1 初级讲解示例(form基本验证,唯一约束验证)

    Form或ModelForm来做一个表单的验证,对应数据库表中的一行数据,而formset是做多个表单验证的组件
    应用场景:批量操作

    用法:

    ==================视图函数================

    from django import forms
    from rbac import models
    from django.forms import formset_factory
    from django.shortcuts import render,HttpResponse

    class MultiPermissionForm(forms.Form):
    '''单个表单验证代码
    标题,URL,NAME,菜单,父权限
    '''
    pass

    def multi_permission_add(request):
    # extra=2表示在前端生成2个表单,
    # 如果一个表单都不填可以提交,不报错,数据库不增加
    formset_class = formset_factory(MultiPermissionForm, extra=2)
    if request.method == 'GET':
    formset=formset_class()
    return render(request, 'multi_add.html', {'formset': formset})
    formset = formset_class(data=request.POST)
    if formset.is_valid():
    '''
    cleaned_data必须放上面,因为如果errors方法执行,
    会导致cleaned_data中没有数据,必须先保存cleaned_data
    原因:能进到这里,数据就是验证通过的。cleaned_data每次取之前要
    检查formset里是否有错误信息,没有才取的出来,如果cleaned_data
    放里面,会检查很多次,直到写入错误信息之后,就检查出错误,取不出来了
    '''
    batch_list = [] # 待批量入库的对象
    flag = True
    post_row_list = formset.cleaned_data
    for i in range(0, formset.total_form_count()):
    row = post_row_list[i]
    if not row:
    continue
    try:
    # models.Permission.objects.create(**row)不要用这种方式,不能进行唯一性检查
    obj = models.Permission(**row)
    obj.validate_unique() #检查当前对象在数据库是否存在唯一
    batch_list.append(obj)
    except Exception as e:
    formset.erros[i].update(e)
    flag = False
    if flag:
    models.Permission.objects.bulk_create(batch_list, batch_size=50) #批量入库每次50条
    return HttpResponse('当次提交通过验证')
    else:
    return render(request,'multi_add.html',{'formset':formset})
    return render(request,'multi_add.html',{'formset':formset})

    ================模板渲染===================

    <form method="post">
    {% csrf_token %}
    {{ formset.management_form }}
    <table>
    <thead>
    <tr>
    <th>标题</th>
    <th>URL</th>
    <th>NAME</th>
    <th>菜单</th>
    <th>父权限</th>
    </tr>
    </thead>
    <tbody>
    {% for form in formset %}
    <tr>
    {% for field in form %}
    <td>{{ field }} <span>{{ field.errors.0 }}</span></td>
    {% endfor %}
    </tr>
    {% endfor %}

    </tbody>
    </table>
    <input type="submit">
    </form>

    6.2 写在前面,如何进行重复字段验证?

    6.3 自我提高(form基本验证,唯一约束验证,formset重复项验证)(方式一:还行,结构不太美观,展示不错,性能可以)

    特点:唯一约束在视图函数中校验,

    models:

    from django.db import models
    class PersonInfo(models.Model):
        name = models.CharField(verbose_name='姓名', max_length=32, unique=True, )
        email = models.EmailField(verbose_name='邮箱')

    views:

    from django.shortcuts import render, HttpResponse
    from django import forms
    from django.forms import Form
    from django.forms import BaseFormSet
    from django.forms import formset_factory
    from app01 import models
    
    
    #####################Form########################################
    class AddForm(Form):
        name = forms.CharField()
        email = forms.EmailField()
    
        def __eq__(self, other):
            '''
            重写该方法为了进行index找索引时按name进行比较查找
            '''
            if self.cleaned_data['name'] == other.cleaned_data['name']:
                return True
    
    
    class NameDistinctFormSet(BaseFormSet):
        def clean(self):
            if any(self.errors):
                return
            temp_names = []  # 进行比较使用的临时列表
            group_dict = {}  # 要返回的结果{'重复项',[行数1,行数2,行数3],...}
            offset = 1  # 索引偏移量
            index = 0  # 索引从0开始
            for form in self.forms:
                if not form.cleaned_data:
                    continue
                name = form.cleaned_data['name']
                if name in temp_names:
                    if name in group_dict:  # 不是第一次了
                        group_dict[name].append(index + offset)
                    else:  # 第一次找到相同的,需要重写Form类的__eq__方法
                        group_dict[name] = [self.forms.index(form) + offset, ]
                        group_dict[name].append(index + offset)
                temp_names.append(name)
                index += 1
            if group_dict:
                # 全局错误通过formset.non_form_errors()取
                raise forms.ValidationError("重复项及行号> " + group_dict.__str__())
    
    
    #############################View##########################################
    def index(request):
        # extra=5表示产生6个空白Form, min_num=6表示数据集减去标记为删除后的最小表单数,
        # 此处表示每次必须提交6个有效表单
        # 此处如果没有写,表示入库的是通过验证且不为空的行
        add_formset_class = formset_factory(AddForm, formset=NameDistinctFormSet, extra=5, min_num=6, validate_min=True)
        add_formset = None
        # 批量添加
        if request.method == 'POST':
            add_formset = add_formset_class(data=request.POST)
            if add_formset.is_valid():
                batch_list = []  # 待写入数据库的数据
                flag = True
                post_row_list = add_formset.cleaned_data
                for i in range(0, add_formset.total_form_count()):
                    row = post_row_list[i]
                    if not row:  # 为空的行就算通过验证也不会入库
                        continue
                    try:
                        obj = models.PersonInfo(**row)
                        obj.validate_unique()  # 检查当前对象在数据库是否存在唯一
                        batch_list.append(obj)
                    except Exception as e:
                        add_formset.errors[i].update(e)
                        flag = False
                if flag:
                    models.PersonInfo.objects.bulk_create(batch_list, batch_size=20)
                    return HttpResponse('成功入库>>%d条' % len(batch_list))
        if request.method == 'GET':
            add_formset = add_formset_class()
        return render(request, 'index.html', {'add_formset': add_formset})

    templates:

    <form method="post">
        {% csrf_token %}
        {{ add_formset.management_form }}
        <table class="table">
            <thead>
            <tr>
                <th class="text-center">姓名(不能重复)</th>
                <th class="text-center">邮箱</th>
            </tr>
            </thead>
            <tbody>
            {% for form in add_formset %}
                <tr>
                    {% for field in form %}
                        <td class="text-center">
                            {{ field }}<br>
                            <span style="color: red;">{{ field.errors.0 }}</span>
                        </td>
                    {% endfor %}
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <div>
            <span style="color: red;">{{ add_formset.non_form_errors.0 }}</span>
            <input type="submit">
        </div>
    </form>

    6.4 自我提高(form基本验证,唯一约束验证,formset重复项验证)(方式二:终极版,结构清晰,展示不错,性能可以)

    跟6.3相比就只是BaseFormSet和视图函数变了。

    from django.shortcuts import render, HttpResponse
    from django import forms
    from django.forms import Form
    from django.forms import BaseFormSet
    from django.forms import formset_factory
    from app01 import models
    
    
    #####################Form########################################
    class AddForm(Form):
        name = forms.CharField()
        email = forms.EmailField()
    
        def __eq__(self, other):
            '''
            重写该方法为了进行index找索引时按name进行比较查找
            '''
            if self.cleaned_data['name'] == other.cleaned_data['name']:
                return True
    
    
    class NameDistinctFormSet(BaseFormSet):
        def clean(self):
            if any(self.errors):
                return
            temp_names = []  # 进行比较使用的临时列表
            group_dict = {}  # 要返回的结果{'重复项',[行数1,行数2,行数3],...}
            offset = 1  # 索引偏移量
            index = 0  # 索引从0开始
            for form in self.forms:
                if not form.cleaned_data:
                    continue
                name = form.cleaned_data['name']
                if name in temp_names:
                    if name in group_dict:  # 不是第一次了
                        group_dict[name].append(index + offset)
                    else:  # 第一次找到相同的,需要重写Form类的__eq__方法
                        group_dict[name] = [self.forms.index(form) + offset, ]
                        group_dict[name].append(index + offset)
                temp_names.append(name)
                index += 1
            if group_dict:
                # 全局错误通过formset.non_form_errors()取
                raise forms.ValidationError("重复项及行号> " + group_dict.__str__())
    
            post_row_list = self.cleaned_data
            for i in range(0, self.total_form_count()):
                row = post_row_list[i]
                if not row:  # 为空的行就算通过验证也不会入库
                    continue
                try:
                    obj = models.PersonInfo(**row)
                    obj.validate_unique()  # 检查当前对象在数据库是否存在唯一
                except Exception as e:
                    self.errors[i].update(e)
    
    
    #############################View##########################################
    def index(request):
        # extra=5表示产生6个空白Form, min_num=6表示数据集减去标记为删除后的最小表单数,
        # 此处表示每次必须提交6个有效表单
        # 此处如果没有写,表示入库的是通过验证且不为空的行
        add_formset_class = formset_factory(AddForm, formset=NameDistinctFormSet, extra=4)
        add_formset = None
        # 批量添加
        if request.method == 'POST':
            add_formset = add_formset_class(data=request.POST)
            # is_valid为真:通过了重复验证和数据库唯一约束验证
            if add_formset.is_valid():
                batch_list = []  # 待写入数据库的数据
                for data in add_formset.cleaned_data:
                    if not data:  # 为空的不入库
                        continue
                    obj = models.PersonInfo(**data)
                    batch_list.append(obj)
                try:
                    models.PersonInfo.objects.bulk_create(batch_list, batch_size=20)
                    return HttpResponse('成功入库>>%d条' % len(batch_list))
                except Exception as e:
                    return HttpResponse('入库错误(绝对不是字段唯一性导致的):%s' % e.__str__())
        if request.method == 'GET':
            add_formset = add_formset_class()
        return render(request, 'index.html', {'add_formset': add_formset})

    七、formset 批量修改(带唯一验证)

    ======================视图函数=======================

    class MultiUpdatePermissionForm(forms.Form):
    '''修改要增加隐藏的ID字段
    隐藏ID,标题,URL,NAME,菜单,父权限
    '''
    id = forms.IntegerField(widget=forms.HiddenInput())
    pass


    def multi_permission_edit(request):
    # extra=0表示不额外增加列,与数据库一致
    formset_class = formset_factory(MultiPermissionForm, extra=0)
    if request.method == 'GET':
    # formset = formset_class(initial=[{'id': 1, 'title': 'a', },
    # {'id': 2, 'title': 'b', }])
    formset = formset_class(
    initial=models.Permission.objects.all().values('id', 'title', 'name', 'url', 'menu_id', 'pid_id')
    )
    return render(request, 'multi_edit.html', {'formset': formset})

    formset = formset_class(data=request.POST)
    if formset.is_valid():
    post_row_list = formset.cleaned_data
    flag = True
    for i in range(0, formset.total_form_count()):
    row = post_row_list[i]
    if not row:
    continue
    permission_id = row.pop('id')
    try:
    permission_obj = models.Permission.objects.filter(id=permission_id).first
    for key,value in row.items():
    setattr(permission_obj,key,value)
    permission_obj.validate_unique()
    permission_obj.save()
    except Exception as e:
    formset.erros[i].update(e)
    flag = False
    if flag:
    return HttpResponse('当次提交通过验证')
    else:
    return render(request, 'multi_edit.html', {'formset': formset})
    return render(request, 'multi_edit.html', {'formset': formset})

    ==================模板渲染======================

    <form method="post">
    {% csrf_token %}
    {{ formset.management_form }}
    <table>
    <thead>
    <tr>
    <th>标题</th>
    <th>URL</th>
    <th>NAME</th>
    <th>菜单</th>
    <th>父权限</th>
    </tr>
    </thead>
    <tbody>
    {% for form in formset %}
    <tr>
    {% for field in form %}
    {% if forloop.first %}
    {{ field }}
    {% else %}
    <td>{{ field }} <span>{{ field.errors.0 }}</span></td>
    {% endif %}
    {% endfor %}
    </tr>
    {% endfor %}

    </tbody>
    </table>
    <input type="submit">
    </form>

    sdf

  • 相关阅读:
    jquery实现京东轮播图的简单写法
    prop方法实现全选效果
    jquery实现点击小图实现大图的案例
    jquery实现淘宝精品图片切换
    html+css实现下拉菜单效果
    jquery对象和DOM对象的联系及转化
    【学习】012 垃圾回收机制算法分析
    【学习】011 JVM参数调优配置
    【学习】010 Netty异步通信框架
    【学习】009 NIO编程
  • 原文地址:https://www.cnblogs.com/staff/p/10735347.html
Copyright © 2011-2022 走看看