• [oldboy-django][2深入django]Form总结


    1 form总结

    # Form数据格式验证
            - 原理:
                - 流程
                a.写类LoginForm(Form):
                    字段名 = fields.xxFields() # 验证规则,本质是正则表达式(fields.xxFields()是一个正则表达式)
                    字段名 = fields.xxFields() # 验证规则,本质是正则表达式
                b. obj = LoginForm(request.POST)
                c. 验证数据result = obj.is_valid()
                d. 拿到符合格式的数据 obj.cleaned_data
                e. 不符合格式,获取错误信息 obj.errors
    
            - Form提交数据验证程序(没有实现保留上次输入的值)
                # 前端
                    <form action="/app02/login" method="POST">
                        {% csrf_token %}
                        <p>
                            <input type="text" name="user" placeholder="用户名">
                            <span style="color:red;">{{ error.user.0 }}</span>
                        </p>
                        <p>
                            <input type="password" name="pwd" placeholder="密码">
                            <span style="color:red;">{{ error.pwd.0 }}</span>
                        </p>
                        <p><input type="submit" value="提交"></p>
                    </form>
    
                # LoginForm类
                class LoginForm(Form):
                    user = fields.CharField(required=True,
                                            error_messages={
                                                    'required': '不能为空',
                                                })
                    pwd = fields.CharField(required=True,
                                           min_length=8,
                                           error_messages={
                                                    'required': '不能为空',
                                                    'min_length': '长度必须大于8'
                                                })
    
    
                # 视图
                def login(request):
                    if request.method == 'GET':
                        return render(request, 'app02_login.html')
                    else:
                        obj = LoginForm(request.POST)
                        # 检验提交数据是否符合规则
                        if obj.is_valid():
                            print(obj.cleaned_data)
                            # obj.cleaned_data是一个字典,form表单提交的数据
                            #{'password': 'aaaaaaaaaa', 'username': 'alexadfdda'}
                            return redirect('/app02/login')
                        else:
                            return render(request, 'app02_login.html', {'error': obj.errors})
    
    # Form保留上一次输入数据
        - 原理
            b.Form提交,验证数据同时保留上次输入的值
            1.生成html标签操作
                - widget  # 选择input的类型,可为Textarea,select
                只要,前端:{{ obj.t1 }}或者 {{obj.as_p}}
                      视图:无论get还是Post都将obj传给前端
    
            2 利用上面的几个参数保留上次输入的内容
                a.原理: 利用Form组件可以生成标签
                    GET:
                        obj = TestForm()
                        {{ obj.t1 }} 等效成 <input type="text" name="t1">
                    POST:
                        obj = TestForm(request.POST)
                        {{ obj.t1}} 等效成<input type="text" name="t1" value = "你提交的数据">
                        {{ obj.errors.t1.0 }} 显示错误信息
    
                     总之, 前端:{{ obj.t1 }}或者 {{obj.as_p}}
                            视图:无论get还是Post都将obj传给前端
        - 实例
    
            # FormL类
            class TestForm(Form):
                t1 = fields.CharField(
                    required=True,
                    min_length=4,
                    max_length=8,
                    widget=widgets.TextInput,
                    label='用户名',
                    label_suffix=':',
                    help_text='4-8个字符',
                    initial='root'
                )
                t2 = fields.CharField(
                    required=True,
                    min_length=8,
                    widget=widgets.PasswordInput,
                    label='密码',
                    label_suffix=':',
                    initial='password'
                )
            #视图
                def test(request):
                    if request.method == 'GET':
                        obj = TestForm()
                        return render(request, 'app02_test.html', {'obj': obj})
                    else:
                        obj = TestForm(request.POST)
                        if obj.is_valid():
                            # 数据库添加数据
                            return redirect("/app02_index.html")
                        else:
                            return render(request, 'app02_test.html', {'obj': obj})
    
            # 前端
                <form action="/app02/test" method="POST" novalidate>
                    {% csrf_token %}
                    {{ obj.as_p }}
                    <p><input type="submit" value="提交"></p>
                </form>
    
    # ajax实现验证数据 + 保留上次输入的值
        - ajax提交(能验证数据 + 保留上次输入的值)
            # 模板
                <form action="/app02/ajax_login" method="POST" id="form1">
                    {% csrf_token %}
                    <p>
                        <input type="text" name="user" placeholder="用户名">
                        <span style="color:red;">{{ error.user.0 }}</span>
                    </p>
                    <p>
                        <input type="password" name="pwd" placeholder="密码">
                        <span style="color:red;">{{ error.pwd.0 }}</span>
                    </p>
                    <p><a onclick="submitForm();">ajax提交数据</a></p>
                </form>
                <script src="/static/js/jquery-1.12.4.js"></script>
                <script>
                    function submitForm() {
                        $(".temp").remove();
                        $.ajax({
                            url: '/app02/ajax_login',
                            type: 'POST',
                            data: $('#form1').serialize(),
                            dataType: 'JSON',
                            success: function (arg) {
                                console.log(arg);
                                if(arg.status){
    
                                }else{
                                    $.each(arg.msg, function (index, value) {
                                        tag = document.createElement('span');
                                        tag.innerHTML = value[0];
                                        tag.style.color = 'red';
                                        tag.className = "temp";
                                        $('#form1').find('input[name="' + index + '"]').after(tag);
    
                                    })
    
                                }
                            }
                        })
                    }
                </script>
    
            # 视图
            def ajax_login(request):
                if requset.method = 'GET':
                    return render(request, 'ajax_login.html')
                else:
                    ret = {'status': True, 'msg': None}
                    obj = LoginForm(request.POST)
                    if obj.is_valid():
                        print(obj.cleaned_data)
                    else:
                        ret['status'] = False
                        ret['msg'] = obj.errors
                    import json
                    return HttpResponse(json.dumps(ret))
                    # 可以返回render, 因为render实际就是调用HttpResponse
    
    
    # Form生成html标签原理
        a. 通过Form生成Input输入框,Form标签,以及submit标签还是要在前端写的,
            但是Form标签内的Input标签可以在后台实现;只需要按以下步骤
            - views定义StudentForm(Form)类
            - views视图函数将Form实例化对象传递给前端
            - 前端{{ obj.段 }}即可
    
        b. 通过Form设置前端Input的type属性,即设置不同类型的输入框
            # 设置name为text, cls_id为下拉框
            class StudentForm(Form):
                name = fields.CharField(widget= widgets.InputText())
                cls_id = fields.IntegerField(widget = widgets.Select)
    
        c. 设置下拉框的内容choices属性
            class StudentForm(Form):
                cls_id = fields.IntegerField(
                    widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'))
                )
            注意: choices的值必须[元组,(), ()]类型
            widget=widgets.Select(choices=[(1, '上海'), (2,'北京')])
    
        d.设置input输入框的class属性 -- attrs
            name = fields.CharField(max_length=8, min_length=2,
                            widget=widgets.TextInput(attrs={'class': 'form-control'})
                            )
            cls_id = fields.IntegerField(
                    widget=widgets.Select(
                                            choices=models.Classes.objects.values_list('id', 'title'),
                                            attrs={'class': 'form-control'}
                                        )
                )
    
            注意: attrs参数必须放在TextInput或者Select等内部,而且值必须为字典
    
    #通过Form设置前端Input的默认显示值原理
            只要在视图函数将实例化一个Form对象,并且设置initial值即可,但对单选和多选有区别
            - 单选
                student_dict = models.Student.objects.filter(id=nid).values('name', 'age', 'email', 'cls_id').first()
                obj = StudentForm(initial=student_dict)
                # initial必须是字典
                return render(request, "a.html", {'obj': obj}
    
    # 多对多时,如何将表单数据插入到数据库中
        - 新增老师的时候,如何将提交的数据插入到数据库两张表上(老师表和第三张表)
            class_list = obj.cleaned_data.pop('cls')
            # 将老师任教班级数据pop出来,此时cleaned_data= {'name': xxx}
            teacher = models.Teacher.objects.create(**obj.cleaned_data)
            teacher.cls.add(*class_list)
    
    # 多对多时,页面如何显示第三张表的数据
        - 添加老师成功后,跳转到teachers页面,如何显示老师任教班级的数据(老师和班级的关联信息是放在第三张表app01_teacher_cls上)
            item.cls是一个objects对象,后面可以接values, all, values_list
                    {% for item in teacher_list %}
                        <tr>
                            <td>{{ item.id }}</td>
                            <td>{{ item.name }}</td>
                            <td>
                                {% for x in item.cls.values_list %}
                                    {{ x.1}}
                                {% endfor %}
                            </td>
    
                    {% endfor %}
    
    
    # BUG:页面刷新时,无法动态显示数据库内容
        分别打开添加老师(或者学生)的页面,和添加班级的页面, 然后再添加班级页面新添加一个班级。
        刷选添加老师(或者学生)页面,发现班级下拉框并没有动态增加刚才新增加的班级。
    
        原因分析:出现在class TeacherForm和StudentForm定义上,以TeacherForm为例
        class TeacherForm(Form):
            name = fields.CharField(max_length=16,
                                    widget=widgets.TextInput(attrs={'class': 'form-control'})
                                    )
            cls = fields.MultipleChoiceField(
                choices=models.Classes.objects.values_list('id', 'title'),
                widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
            )
    
        在实例化一个TeacherForm对象时,由于name, cls为类变量,所以这两个类变量只要第一次生成后,
        后面实例化对象时,这两个变量是不会改变的。
        在调用父类init函数时,会将cls, name放到父类的self.fields里面
        self.fields = {'name': name, 'cls': cls}
    
        因此解决办法出来了,在每一次实例化对象时,再获取数据库的值给cls,
        重新刷新self.fields里面的cls字段内容
        class TeacherForm(Form):
            name = fields.CharField(max_length=16,
                                    widget=widgets.TextInput(attrs={'class': 'form-control'})
                                    )
            cls = fields.MultipleChoiceField(
                choices=models.Classes.objects.values_list('id', 'title'),
                # 多选这个可不能删,因为下面的init修改的不是这里
                widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
            )
    
            def __init__(self, *args, **kwargs):
                super(TeacherForm, self).__init__(*args, **kwargs)
                self.fields['cls'].widget.choices = models.Classes.objects.values_list('id', 'title')
    
    
    # form生成下拉框多选 + 动态刷新
        - 多选
            class TeacherForm(Form):
                name = fields.CharField(max_length=16,
                                        widget=widgets.TextInput(attrs={'class': 'form-control'})
                                        )
                cls = fields.MultipleChoiceField(
                    choices=models.Classes.objects.values_list('id', 'title'),
                    widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
                )
    
                def __init__(self, *args, **kwargs):
                    super(TeacherForm, self).__init__(*args, **kwargs)
                    self.fields['cls'].choices = models.Classes.objects.values_list('id', 'title')
    
    # form生成下拉框单选 + 动态刷新
        class StudentForm(Form):
            name = fields.CharField(max_length=16,
                                    widget=widgets.TextInput(attrs={'class': 'form-control'})
                                    )
            cls = fields.ChoiceField(
                choices=models.Classes.objects.values_list('id', 'title'),
                widget=widgets.Select(attrs={'class': 'form-control'})
    
            def __init__(self, *args, **kwargs):
                super(StudentForm, self).__init__(*args, **kwargs)
                self.fields['cls'].choices = models.Classes.objects.values_list('id', 'title')
    
    
    # form设置下拉框多选的默认值
        - initial参数,值必须为字典。
            row = models.teacher.objects.filter(id=nid).first()
            obj = TeacherForm(initial={'name': row.name, 'cls': [1,2]})
    
            # 在渲染编辑页面的时候,将obj传递给前端即可
            # 前端 {{obj.name}} {{obj.cls}}
    
    # form设置下拉框单选的默认值
        row = models.student.objects.filter(id=nid).first()
        obj = StudentForm(
            initial={
                    'name': row.name,
                    'age': row.age,
                    'email': row.email,
                    'cls_id': row.cls_id
            }
    
         字段太多,可以用另外一种比较好的方式:
         row_dict = models.student.objects.filter(id=nid).values('id','age','email','cls_id')
         obj = StudentForm(initial=row_dict)
    
    # Form生成常见的标签
        class TestFieldForm(Form):
            t1 = fields.CharField(
                widget=widgets.TextInput
            )
            t2 = fields.CharField(widget=widgets.Textarea)
    
            # 结合起来就是,下拉框的单选, 单选用ChoiceField,
            # widget是下拉框,radio, checkbox等
            t3 = fields.ChoiceField(
                choices=[(1, ''), (2, '')],
                widget=widgets.Select()
            )
    
            # 下拉框的多选
            t4 = fields.MultipleChoiceField(
                choices=[(1, '篮球'), (2, '足球'), (3, '控球')],
                widget=widgets.SelectMultiple(attrs={})
    
            )
    
            # radio单选
            t5 = fields.ChoiceField(
                choices=[(1, ''), (2, '')],
                widget=widgets.RadioSelect)
    
            # checkbox多选
            t6 = fields.MultipleChoiceField(
                choices=[(1, '篮球'), (2, '足球'), (3, '控球')],
                widget=widgets.CheckboxSelectMultiple)
    
            # 上传文件类型
            t7 = fields.FileField(
                widget=widgets.FileInput
            )
    
    
        def test_fields(request):
            obj = TestFieldForm(initial={'t1': '姓名'})
            return render(request, 'app01_test_fields.html', {'obj': obj})
    
    # Form扩展--自定义数据验证
        - RegxField
        - 字段 = xxField(validators=)
        - 钩子(增加一些高级验证)
            a clean_xx  -- 对特定的字段进行验证
            b clean     -- 对所有字段(或者两个或以上字段)进行验证
    
        - 实例
            # 自定义数据验证
            from django.core.exceptions import ValidationError
            class TestForm(Form):
                user = fields.CharField(
                    widget=widgets.TextInput
                )
                pwd = fields.CharField(widget=widgets.PasswordInput)
                email = fields.EmailField()
    
                age = fields.IntegerField(min_value=18,max_value=30)
    
                def clean_user(self):
                    # 增加验证用户名不能重复
                    # 这里不能获取密码的值即不能self.cleaned_data['pwd']
                    user_post = self.cleaned_data['user']
                    print(user_post)
                    if models.Student.objects.filter(name=user_post).count():
                        raise ValidationError('用户名已经存在')
                    else:
                        return self.cleaned_data['user']
    
                def clean_pwd(self):
                    # 也可以自定制其他的验证
                    return self.cleaned_data['pwd']
    
                def clean(self):
                    # 对字段间进行验证: 比如用户名和email不能重复
                    # 到了这里不能说明已经通过clean_xx验证,所以要用get
    
                    age_post = self.cleaned_data.get('age')
                    print(age_post)
                    email_post = self.cleaned_data.get('email')
                    print(email_post)
                    if models.Student.objects.filter(age=age_post, email=email_post).count():
                        raise ValidationError('年龄和邮箱组合已经存在', code='com')
    
                    return self.cleaned_data
            # 视图
            def test(request):
                if request.method == 'GET':
                    obj = TestForm()
                    return render(request, 'app01_test.html',{'obj': obj} )
                else:
                    obj = TestForm(request.POST)
                    if obj.is_valid():
                        print('合格')
                    else:
                        print('不合格')
                        com = obj.errors['__all__']
                    return render(request, 'app01_test.html',{'obj': obj, 'com':com})
    
            # 前端
                <!DOCTYPE html>
                    <html lang="en">
                    <head>
                        <meta charset="UTF-8">
                        <title>Title</title>
                    </head>
                    <body>
                    <form action="/app01/test" method="POST">
                        {% csrf_token %}
                        {{ obj.user }}{{ obj.errors.user.0 }}
                        {{ obj.pwd }} {{ obj.errors.pwd.0 }}
                        {{ obj.email }} {{ obj.errors.email.0 }}
                        {{ obj.age }} {{ obj.errors.age.0 }}
                        {{ com }}
                        <input type="submit" value="提交">
                    </form>
                    </body>
                    </html>
    
    
    总结:
        1 使用
            class FooForm(Form):
                xx = fields.xxFields()
    
                def clean_xx(self):
    
                    return self.cleaned_data['xx']
    
                def clean()
                    return self.cleaned_data
        2 页面展示
            {{ obj.xx}}
    
        3 后台
            表单渲染
                obj = FooForm(initial={'name':xx})
                return render(request, 'xx.html', {'obj': obj})
            表单提交
                obj = FooForm()
                if obj.is_valid():
                    # 插入数据库
                    return redirect()
                else:
                    return render(request, 'xx.html', {'obj': obj})
    View Code

    2 补充自定义form里面的clean,__init__, clean_xx

    def login_view(request):
        #http://127.0.0.1:8000/alice/login/?next=/p/
        redirect_to = request.POST.get("next", request.GET.get("next", ""))
        print "alice: redirect to: "; redirect_to
        
        if request.method == 'POST':
            form = LoginForm(request, data=request.POST)
    
            if form.is_valid():
                mobile = form.cleaned_data['mobile']
                #password = form.cleaned_data['password']
                profile = Profile.objects.get(mobile=mobile)
                login(request, form.get_user())
                #redirect_to = '/p/' #alice: TBD: str(profile.id)
                request.session['profile_id'] = profile.id
                if redirect_to != '':
                    # Ensure the user-originating redirection url is safe.
                    if not is_safe_url(url=redirect_to, host=request.get_host()):
                        redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)                
                    return HttpResponseRedirect(redirect_to)
                else:
                    return JsonResponse({'status':'1','id':profile.id, 'name':profile.personal_info.name})                
            else:
                #print(form.errors.as_json())
                json_str = form.errors.as_json()
                return JsonResponse({'status':'0', 'error': json_str})
                
        
        else:
            form = LoginForm()
            return render(request, 'registration/login.html', {'form':form, 'next':redirect_to})
    View Code

    3 wupeiqi form总结网站

    http://www.cnblogs.com/wupeiqi/articles/6144178.html

    4 推荐一个学习form的中文网站

    http://www.yiibai.com/django/django_form_processing.html

  • 相关阅读:
    Cesium中的坐标系及转换
    Cesium Workshop
    window.postMessage 跨窗口,跨iframe javascript 通信
    VUE课程参考---7、跑马灯效果
    VUE课程---9、事件绑定v-on
    VUE课程---8、属性绑定v-bind
    VUE课程---7、解决插值表达式闪烁问题
    小谈chrome调试命令:console.log的使用
    Hadoop平台配置总结
    hadoop 关闭进程时报错no 进程 to stop
  • 原文地址:https://www.cnblogs.com/liuzhipenglove/p/7874004.html
Copyright © 2020-2023  润新知