django框架进阶-form组件-长期维护

##################     form组件做了什么事情?      #######################

之前web开发的模式,以注册为例

1,要有一个注册页面,然后有一个form表单 ---->html代码
2,数据提交到后端,做校验, ---->数据有效性校验,
3,把校验的提示信息,展示在页面上,--->校验信息返回,并保存原来的值

关于校验:
1,前端通过js校验,比如数据输没输,格式是否正确,都是可以前端校验的,
2,数据准确性的需要后端做,
这两个都是前端可有可没有,但是后端一定要有校验,前端的js可以被绕过,而且可以禁用,所以后端一定要有校验,
最好前后端都校验,这样一部分前端可以校验的,可以减轻服务端的压力,因为有些不需要往后端发请求就可以校验了,

使用form表单,就做了这三个事情,可以使得代码更加的简洁,快速开发

 

##################    不使用form组件--以注册为例       #######################

视图:

# 注册
def register(request):
    error_msg = ""
    if request.method == "POST":
        username = request.POST.get("name")
        pwd = request.POST.get("pwd")
        # 对注册信息做校验
        if len(username) < 6:
            # 用户长度小于6位
            error_msg = "用户名长度不能小于6位"
        else:
            # 将用户名和密码存到数据库
            return HttpResponse("注册成功")
    return render(request, "register.html", {"error_msg": error_msg})

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<form action="/reg/" method="post">
    {% csrf_token %}
    <p>
        用户名:
        <input type="text" name="name">
    </p>
    <p>
        密码:
        <input type="password" name="pwd">
    </p>
    <p>
        <input type="submit" value="注册">
        <p style="color: red">{{ error_msg }}</p>
    </p>
</form>
</body>
</html>

 

##################     使用form组件的注册案例       #######################

 form类:

from django import forms

# 按照Django form组件的要求自己写一个类
class RegForm(forms.Form):
    name = forms.CharField(label="用户名")
    pwd = forms.CharField(label="密码")

视图类:

# 使用form组件实现注册方式
def register2(request):
    form_obj = RegForm()
    if request.method == "POST":
        # 实例化form对象的时候,把post提交过来的数据直接传进去
        form_obj = RegForm(request.POST)
        # 调用form_obj校验数据的方法
        if form_obj.is_valid():
            return HttpResponse("注册成功")
    return render(request, "register2.html", {"form_obj": form_obj})

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册2</title>
</head>
<body>
    <form action="/reg2/" method="post" novalidate autocomplete="off">
        {% csrf_token %}
        <div>
            <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
            {{ form_obj.name }} {{ form_obj.name.errors.0 }}
        </div>
        <div>
            <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
            {{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
        </div>
        <div>
            <input type="submit" class="btn btn-success" value="注册">
        </div>
    </form>
</body>
</html>

 

##################     form组件的使用     #######################

 视图:

#          Django Form组件的使用
from django import forms
from django.forms import widgets
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class RegForm(forms.Form):  # 使用form组件一定要定义一个对应的form类,
    name = forms.CharField(
        # 校验规则相关
        max_length=16,
        label="用户名",
        error_messages={
            "required": "该字段不能为空",
        },
        # widget控制的是生成html代码相关的
        # widget=widgets.TextInput()
        widget=widgets.TextInput(attrs={"class": "form-control"})  # 增加属性
    )
    pwd = forms.CharField(
        label="密码",  # 定义标签的名字,
        min_length=6,
        max_length=10,
        widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True),
        # widgets.PasswordInput,这个输入框的css的type类型就是password的了,可以隐藏输入的内容
        # render_value=True,在报错的时候,会把密码重新填写回来,

        error_messages={
            "min_length": "密码不能少于6位!",  # 定义错误信息
            "max_length": "密码最长10位!",
            "required": "该字段不能为空",
        }
    )
    re_pwd = forms.CharField(
        label="确认密码",
        min_length=6,
        max_length=10,
        widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True),
        error_messages={
            "min_length": "密码不能少于6位!",
            "max_length": "密码最长10位!",
            "required": "该字段不能为空",
        }
    )

    email = forms.EmailField(
        label="邮箱",

        widget=widgets.EmailInput(attrs={"class": "form-control"}),
        error_messages={
            "required": "该字段不能为空",
        }
    )

    mobile = forms.CharField(
        label="手机",
        # 自己定制校验规则
        validators=[
            RegexValidator(r'^[0-9]+$', '手机号必须是数字'),
            RegexValidator(r'^1[3-9][0-9]{9}$', '手机格式有误')
        ],
        widget=widgets.TextInput(attrs={"class": "form-control"}),
        error_messages={
            "required": "该字段不能为空",
        }
    )

    city = forms.ChoiceField(
        choices=models.City.objects.all().values_list("id", "name"),
        label="城市",
        initial=1,  # 设置默认值
        widget=forms.widgets.Select  # 下拉选项
    )

    # 重写父类的init方法
    # 如果一个字段的内容是从数据库获取的,需要重写父类的init方法
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["city"].widget.choices = models.City.objects.all().values_list("id", "name")


    def clean_name(self):
        value = self.cleaned_data.get("name")
        if "金瓶1梅" in value:
            raise ValidationError("不符合社会主义核心价值观!")
        return value

    # 重写父类的clean方法
    def clean(self):
        # 此时 通过检验的字段的数据都保存在 self.cleaned_data
        pwd = self.cleaned_data.get("pwd")
        re_pwd = self.cleaned_data.get("re_pwd")
        if pwd != re_pwd:
            self.add_error("re_pwd", ValidationError("两次密码不一致"))
            raise ValidationError("两次密码不一致")
        return self.cleaned_data
    # gender = forms.ChoiceField(
    #     choices=((1, "男"), (2, "女"), (3, "保密")),
    #     label="性别",
    #     initial=1,
    #     widget=forms.widgets.RadioSelect  # 单选
    # )
    # hobby = forms.ChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=3,
    #     widget=forms.widgets.Select  # 单选
    # )
    # hobby2 = forms.MultipleChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=[1, 3],
    #     widget=forms.widgets.SelectMultiple()  # 多选
    # )
    #
    # keep = forms.ChoiceField(
    #     label="是否记住密码",
    #     initial="checked",
    #     widget=forms.widgets.CheckboxInput  # 单选复选框,是否记住密码
    # )
    # hobby3 = forms.MultipleChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=[1, 3],
    #
    #     widget=forms.widgets.CheckboxSelectMultiple(attrs={"class": "c1"})  # 多选复选框
    # )

def reg2(request):
    form_obj = RegForm()

    if request.method == "POST":
        form_obj = RegForm(request.POST)
        # 让form帮我们做校验
        if form_obj.is_valid():  # 这是一个布尔值
            # 校验通过
            # 把数据存到数据库
            # 所有经过校验的数据都保存在 form_obj.cleaned_data
            print(form_obj.cleaned_data)  # 这是一个字典,
            del form_obj.cleaned_data["re_pwd"]
            models.UserInfo.objects.create(**form_obj.cleaned_data)
            return HttpResponse("注册成功!")
        print(form_obj.errors["__all__"][0])
        print(form_obj.errors["re_pwd"])

    return render(request, "reg2.html", {"form_obj": form_obj})

html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1 {
            list-style-type: none;
        }
    </style>

    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>

{#<form action="/reg2/" method="post" novalidate>#}
{#    {% csrf_token %}#}
{#    {{ form_obj.as_p }}#}
{#    {{ form_obj.errors.pwd }}#}
{#    <p><input type="submit"></p>#}
{#</form>#}

<!-- 自己逐个字段写 -->

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="/reg2/" method="post" novalidate>
                {% csrf_token %}
                <div class="form-group {% if form_obj.name.errors.0 %}has-error{% endif %}">
                    {{ form_obj.name.label }}
                    {{ form_obj.name }}
                    <span class="help-block">{{ form_obj.name.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.pwd.errors.0 %}has-error{% endif %}">
                    {{ form_obj.pwd.label }}
                    {{ form_obj.pwd }}
                    <span class="help-block">{{ form_obj.pwd.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.re_pwd.errors.0 %}has-error{% endif %}">
                    {{ form_obj.re_pwd.label }}
                    {{ form_obj.re_pwd }}
                    <span class="help-block">{{ form_obj.re_pwd.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.email.errors.0 %}has-error{% endif %}">
                    {{ form_obj.email.label }}
                    {{ form_obj.email }}
                    <span class="help-block">{{ form_obj.email.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.mobile.errors.0 %}has-error{% endif %}">
                    {{ form_obj.mobile.label }}
                    {{ form_obj.mobile }}
                    <span class="help-block">{{ form_obj.mobile.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.city.errors.0 %}has-error{% endif %}">
                    {{ form_obj.city.label }}
                    {{ form_obj.city }}
                    <span class="help-block">{{ form_obj.city.errors.0 }}</span>
                </div>

                <div class="form-group">
                    <input type="submit" class="btn btn-default">
                </div>

            </form>
        </div>
    </div>

</div>


</body>
</html>

 

 ##################     form组件的使用     #######################

字段校验

RegexValidator验证器

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

自定义验证函数

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
 
 
# 自定义验证规则
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')
 
 
class PublishForm(Form):
 
 
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '标题不能为空',
                                            'min_length': '标题最少为5个字符',
                                            'max_length': '标题最多为20个字符'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': '标题5-20个字符'}))
 
 
    # 使用自定义验证规则
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手机不能为空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

 

 ##################     form组件的使用     #######################

 Hook方法

除了上面两种方式,我们还可以在Form类中定义钩子函数,来实现自定义的验证功能。

局部钩子

我们在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )
    ...
    # 定义局部钩子,用来校验username字段
    def clean_username(self):
        value = self.cleaned_data.get("username")
        if "666" in value:
            raise ValidationError("光喊666是不行的")
        else:
            return value

全局钩子

我们在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验。

class LoginForm(forms.Form):
    ...
    password = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    re_password = forms.CharField(
        min_length=6,
        label="确认密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    ...
    # 定义全局的钩子,用来校验密码和确认密码字段是否相同
    def clean(self):
        password_value = self.cleaned_data.get('password')
        re_password_value = self.cleaned_data.get('re_password')
        if password_value == re_password_value:
            return self.cleaned_data
        else:
            self.add_error('re_password', '两次密码不一致')
            raise ValidationError('两次密码不一致')

 

 ##################     form组件的使用     #######################

 ##################     form组件的使用     #######################

 ##################     form组件的使用     #######################

posted @ 2020-01-13 12:11  技术改变命运Andy  阅读(175)  评论(0编辑  收藏  举报