zoukankan      html  css  js  c++  java
  • Django-form表单

    构建一个表单

      假设你想构建一个登录页面,以获得用户名和密码,你需要类似的模板

    <form action="" method="post">
        <input type="text" name="username">用户名
        <input type="password" name="pwd">密码
        <input type="submit">
    </form>

      我们可能在表单提交之前,在浏览器端做一些验证。我们可能使用非常复杂的字段来完成验证(比如用户名不能为空,密码不含特殊字符等),这个时候,Django来为我们完成大部分工作是很容易的,它的优点在于:

      (1)form表单 提交时,数据出现错误,返回的页面中仍可以保留之前输入的数

      (2)可以方便的限制字段条件

    在Django中构建一个表单

    Form类

    class UserForm(forms.Form):
        user = forms.CharField(max_length=10)
        pwd = forms.CharField(max_length=10)

      自定义类UserForm继承了forms.Form类,该类带有两个字段,字段允许的最大长度通过max_length来定义。首先,它在HTML的<input>上放置一个max_length='10'(这样浏览器将在第一时间阻止用户输入多于这个数目的字符),它还意味着当Django收到浏览器发送过来的表单时,它将验证数据的长度。

      Form的实例具有一个is_valid方法,它为所有的字段运行验证的程序,当调用这个方法时,如果所有的字段都包含合法的数据,它将:返回True,并将表单的数据放到cleaned_data属性中。

    views

    from django.shortcuts import render,HttpResponse
    # Create your views here.
    def login(request): if request.method == 'POST': print(request.POST) #< QueryDict: {'pwd': [''], 'user': ['']} > form = UserForm(request.POST) if form.is_valid(): print("cleaned_data",form.cleaned_data) print("errors",form.errors) return HttpResponse('OK') else: print("cleaned_data", form.cleaned_data) #cleaned_data {} print("errors", form.errors) #errors < ul class ="errorlist" > < li > pwd < ul class ="errorlist" > < li > This field is required.< / li > < / ul > < / li > < li > user < ul class ="errorlist" > < li > This fi print('form.errors["user"]',form.errors["user"]) #form.errors["user"] < ul class ="errorlist" > < li > This field is required.< / li > < / ul > print('form.errors["user"][0]',form.errors["user"][0]) #form.errors["user"][0] Thisfield is required. return render(request,'login.html',{"form":form}) form = UserForm() return render(request,'login.html',{"form":form})

     发送给Django网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图,这允许我们重用一些相同的逻辑,当处理表单时,我们需要在视图中实例化它。

      如果访问视图的是一个GET请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中,这是我们在第一个访问该URL时预期发生的情况。

      如果表单的提交使用POST请求,那视图将再次创建一个表单实例并使用请求中的数据来填充它:form=UserForm(request.POST),这叫做绑定数据至表单(它现在是一个绑定的表单)。

      我们调用表单的is_valid()方法:如果它不为True,我们将带着这个表单返回到模板,这时表单不再为空,所以HTML表单将之前提交的数据填充,然后可以根据要求编辑并改正它。

      如果is_valid()为True:我们将能够在cleaned_data属性中找到所有合法的表单数据,在发送HTTP重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或做其他处理。

    Form表单类详解

      未绑定的表单没有关联的数据,当渲染给用户时,它将为空或包含默认的值

      绑定的表单具有提交的数据,因此可以用来检验数据是否合法。如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据

    #forms.py
    
    from django import forms
    
    class RegisterForm(forms.Form):
        username = forms.CharField(max_length=100,
                                   error_messages={"min_length":"最短为5个字符","required":"该字段不能为空"},
                                   )
        password = forms.CharField(max_length=100,
                                   widget=widgets.PasswordInput(attrs={"placeholder":"password"})
                                    )
    
        telephone=forms.IntegerField(
            error_messages={
                "invalid":"格式错误"
            }
    
                                    )
    
    
        gender=forms.CharField(
              initial=2,
              widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
                 )
    
        email = forms.EmailField()
        is_married = forms.BooleanField(required=False)
    一个更有用的注册表单

      widgets:每个表单都有一个对应的Widget类,它对应一个HTML表单Widget,在大部分情况下,字段都有一个合理的默认Widget

      字段的数据:不管表单提交的是什么数据,一旦通过is_valid()成功验证,验证后的表单数据位于form,cleaned_data字典中,这些数据已经为你转换好python的类型。此时,你依然可以从request.POST中直接访问到未验证的数据,但是访问验证后的数据更好一些。

      在上面的表单示例中,is_married将是一个布尔值,类似的,IntergerField和FloatField字段分别将值转换为python的int和float。

      Form组件的钩子

    class UserForm(forms.Form):
        username = forms.CharField(min_length=4,
                                   max_length=10,
                                   error_messages={
                                       "required":"用户名不能为空",
                                       "max_length":"用户名不能超过10位",
                                       "min_length":"用户名不能少于4位",
                                   })
        password = forms.CharField(min_length=6,
                                   error_messages={
                                       "required":"密码不能为空",
                                       "min_length":"密码不能少于6位"
                                   })
        repeat_pwd = forms.CharField(error_messages={
            "required":"确认密码不能为空"
        })
        email = forms.EmailField(error_messages={
            "invalid":"格式错误",
            "required":"邮箱不能为空"
        })
    
        # 局部钩子
        def clean_username(self):
            value = self.cleaned_data.get('username')
            if  UserInfo.objects.filter(username=value).exists():
                raise ValidationError('用户已存在')
            else:
                return value
    
        # 全局钩子
        def clean(self):
            pwd = self.cleaned_data.get("password")
            repeat_pwd = self.cleaned_data.get("repeat_pwd")
            if pwd == repeat_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("密码不一致")
    views
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/static/bootstrap.min.css">
        <title>reg</title>
        <style>
            body {
                background-color: #0378bb4a;
            }
    
            .form-horizontal {
                border: 20px solid #0378bb1a;
                padding-top: 30px;
                padding-bottom: 30px;
                padding-right: 15px;
                background-color: white;
            }
    
            .avator_img {
                width: 60px;
                height: 60px;
                margin-left: 20px;
            }
    
            #avatar {
                display: none;
            }
    
            span {
                font-size: 10px;
                margin-top: 5px;
            }
        </style>
    
    </head>
    <body>
    {% csrf_token %}
    <div class="container">
        <div class="row">
            <div class="col-md-5 col-md-offset-6" style="margin-top:50px">
    
                <form class="form-horizontal">
                    <div style="padding-bottom: 10px;"><h4 class="text-center">注册账户</h4></div>
    
                    {###############################用户名#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
                            <input type="text" name='name12' class=" form-control" id="username" placeholder="用户名">
                            <span class="pull-right text-danger error"></span>
                        </div>
                    </div>
                    {###############################密码#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
                            <input type="password" name='pwd12' class="form-control" id="password" placeholder="密码">
                            <span class="pull-right text-danger error"></span>
                        </div>
                    </div>
    
                    {###############################重复密码#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
                            <input type="password" name='pwd' class="form-control" id="repeat_pwd" placeholder="重复密码">
                            <span class="pull-right text-danger error" ></span>
                        </div>
                    </div>
    
                    {###############################邮箱#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
                            <input type="email" name='email' class="form-control" id="email" placeholder="邮箱">
                            <span class="pull-right text-danger error"></span>
                        </div>
                    </div>
    
                    {###############################头像#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
    
                            <label for="avatar"> 上传头像<img src="/static/img/default.png" alt="" class="avator_img"></label>
                            <input type="file" id="avatar" class="form-control">
                        </div>
                    </div>
    
                    {###############################注册#########################################}
                    <div class="form-group">
                        <div class="col-sm-offset-1 col-sm-10">
                            <button type="button" class="btn btn-primary btn-block" id="reg_btn">注册</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
    
    
    <script src="/static/jquery-3.3.1.min.js"></script>
    <script src="/static/bootstrap.min.js"></script>
    
    <script>
        {#头像预览#}
        $("#avatar").change(function () {
            var reader = new FileReader();
            var choose_file = $(this)[0].files[0];
            reader.readAsDataURL(choose_file);
            reader.onload = function () {
                $(".avator_img").attr("src", reader.result)
            };
        });
    
        {#注册#}
        $("#reg_btn").click(function () {
            var formdata = new FormData();
            formdata.append("username", $("#username").val());
            formdata.append("password", $("#password").val());
            formdata.append("repeat_pwd", $("#repeat_pwd").val());
            formdata.append("email", $("#email").val());
            formdata.append("avatar", $("#avatar")[0].files[0]);
            formdata.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());
    
    
            $.ajax({
                url: '/reg/',
                type: "post",
                contentType: false,
                processData: false,
                data: formdata,
                success: function (data) {
                    var data=JSON.parse(data);
                    if (data.user) {
                        location.href="/login/"
                    }
                    else {
                        {#清空操作#}
                        $("span.error").html("");
                        console.log(data.msg);
                        var errors=data.msg;
                        for ( var i in errors){
                            if (i==="__all__"){
                                $("#repeat_pwd").next().text(errors[i][0])
                            }
                            else {
                                $('#'+i).next().text(errors[i][0])
                            }
                            }
                        }
                    }
                })
    
    
            })
    
    
    
    </script>
    
    </body>
    </html>
    template

    template

      手工渲染字段模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>login</title>
    
    </head>
    <body>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <p><input type="text" name="user">用户名 <span>{{ form.errors.user.0 }}</span></p>
        <p><input type="password" name="pwd">密码 <span>{{ form.errors.pwd.0 }}</span></p>
        <p><input type="submit"></p>
    </form>
    </body>
    </html>

      由form表单提供的模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>login</title>
    </head>
    <body>
    {#第一种#}
    <form action="" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit">
    </form>
    
    {#第二种#}
    <form action="" method="post">
        {% csrf_token %}
        <div>
            用户名{{ form.user }}
        </div>
        <div>
            密码{{ form.pwd }}
        </div>
        <div>
            <input type="submit">
        </div>
    </form>
    
    {#第三种#}
    <form action="" method="post">
       {% csrf_token %}
        {% for field in form %}
            <div>
            <lable for="">{{ field.label }}</lable>
            {{ field }}
            </div>
        {% endfor %}
        <input type="submit">
    </form>
    </body>
    </html>

       对于<lable>/<input>对,还有几个输出选项: 

    {{ form.as_table }}     {#以表格的形式将它们渲染在<tr>标签中#}
    {{ form.as_p }}       {#将它们渲染在<p>标签中#}
    {{ form.as_ul }}       {#将它们渲染在<li>标签中#}
    {#你必须自己提供<table><ul>元素#}

      渲染表单的错误信息

    {{ form.errors}}
    {{ form.errors.user.0 }}

    form组件补充

      Django内置字段如下

    Field
        required=True,               是否允许为空
        widget=None,                 HTML插件
        label=None,                  用于生成Label标签或显示内容
        initial=None,                初始值
        help_text='',                帮助信息(在标签旁边显示)
        error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
        show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
        validators=[],               自定义验证规则
        localize=False,              是否支持本地化
        disabled=False,              是否可以编辑
        label_suffix=None            Label内容后缀
     
     
    CharField(Field)
        max_length=None,             最大长度
        min_length=None,             最小长度
        strip=True                   是否移除用户输入空白
     
    IntegerField(Field)
        max_value=None,              最大值
        min_value=None,              最小值
     
    FloatField(IntegerField)
        ...
     
    DecimalField(IntegerField)
        max_value=None,              最大值
        min_value=None,              最小值
        max_digits=None,             总长度
        decimal_places=None,         小数位长度
     
    BaseTemporalField(Field)
        input_formats=None          时间格式化   
     
    DateField(BaseTemporalField)    格式:2015-09-01
    TimeField(BaseTemporalField)    格式:11:12
    DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     
    DurationField(Field)            时间间隔:%d %H:%M:%S.%f
        ...
     
    RegexField(CharField)
        regex,                      自定制正则表达式
        max_length=None,            最大长度
        min_length=None,            最小长度
        error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
     
    EmailField(CharField)      
        ...
     
    FileField(Field)
        allow_empty_file=False     是否允许空文件
     
    ImageField(FileField)      
        ...
        注:需要PIL模块,pip3 install Pillow
        以上两个字典使用时,需要注意两点:
            - form表单中 enctype="multipart/form-data"
            - view函数中 obj = MyForm(request.POST, request.FILES)
     
    URLField(Field)
        ...
     
     
    BooleanField(Field)  
        ...
     
    NullBooleanField(BooleanField)
        ...
     
    ChoiceField(Field)
        ...
        choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
        required=True,             是否必填
        widget=None,               插件,默认select插件
        label=None,                Label内容
        initial=None,              初始值
        help_text='',              帮助提示
     
     
    ModelChoiceField(ChoiceField)
        ...                        django.forms.models.ModelChoiceField
        queryset,                  # 查询数据库中的数据
        empty_label="---------",   # 默认空显示内容
        to_field_name=None,        # HTML中value的值对应的字段
        limit_choices_to=None      # ModelForm中对queryset二次筛选
         
    ModelMultipleChoiceField(ModelChoiceField)
        ...                        django.forms.models.ModelMultipleChoiceField
     
     
         
    TypedChoiceField(ChoiceField)
        coerce = lambda val: val   对选中的值进行一次转换
        empty_value= ''            空值的默认值
     
    MultipleChoiceField(ChoiceField)
        ...
     
    TypedMultipleChoiceField(MultipleChoiceField)
        coerce = lambda val: val   对选中的每一个值进行一次转换
        empty_value= ''            空值的默认值
     
    ComboField(Field)
        fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                                   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
     
    MultiValueField(Field)
        PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
     
    SplitDateTimeField(MultiValueField)
        input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
        input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
     
    FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
        path,                      文件夹路径
        match=None,                正则匹配
        recursive=False,           递归下面的文件夹
        allow_files=True,          允许文件
        allow_folders=False,       允许文件夹
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text=''
     
    GenericIPAddressField
        protocol='both',           both,ipv4,ipv6支持的IP格式
        unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
     
    SlugField(CharField)           数字,字母,下划线,减号(连字符)
        ...
     
    UUIDField(CharField)           uuid类型
        ...
    View Code

      Django内置插件

    TextInput(Input)
    NumberInput(TextInput)
    EmailInput(TextInput)
    URLInput(TextInput)
    PasswordInput(TextInput)
    HiddenInput(TextInput)
    Textarea(Widget)
    DateInput(DateTimeBaseInput)
    DateTimeInput(DateTimeBaseInput)
    TimeInput(DateTimeBaseInput)
    CheckboxInput
    Select
    NullBooleanSelect
    SelectMultiple
    RadioSelect
    CheckboxSelectMultiple
    FileInput
    ClearableFileInput
    MultipleHiddenInput
    SplitDateTimeWidget
    SplitHiddenDateTimeWidget
    SelectDateWidget
    View Code

      常用选择插件

    # 单radio,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单radio,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.RadioSelect
    # )
     
    # 单select,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单select,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.Select
    # )
     
    # 多选select,值为列表
    # user = fields.MultipleChoiceField(
    #     choices=((1,'上海'),(2,'北京'),),
    #     initial=[1,],
    #     widget=widgets.SelectMultiple
    # )
     
     
    # 单checkbox
    # user = fields.CharField(
    #     widget=widgets.CheckboxInput()
    # )
     
     
    # 多选checkbox,值为列表
    # user = fields.MultipleChoiceField(
    #     initial=[2, ],
    #     choices=((1, '上海'), (2, '北京'),),
    #     widget=widgets.CheckboxSelectMultiple
    # )
    View Code

    ModelForm

      这个组件的功能就是把model和form组合起来

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from django.views import View
    from django import forms
    from .models import *
    from django.forms import ModelForm
    
    
    class BookForm(forms.Form):
        title=forms.CharField()
        price=forms.DecimalField()
        publishDate=forms.DateField()
        state=forms.ChoiceField(choices=[(1,'已出版'),(2,'未出版')])
        publish=forms.ModelChoiceField(queryset=Publish.objects.all())
        authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all())
    
    class AddBookView(View):
        def get(self,request):
            form=BookForm()
            return render(request,'add_book.html',locals())
    
        def post(self,request):
            form=BookForm(request.POST)
            if form.is_valid():
                form.cleaned_data.pop("authors")
                Book.objects.create(**form.cleaned_data)
    
                return HttpResponse("OK")
            else:
                print(form.cleaned_data)
                print(form.errors)
    
            return HttpResponse('OK')
    form
    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from django.views import View
    from django import forms
    from .models import *
    from django.forms import ModelForm
    
    class BookModelForm(ModelForm):
        class Meta:
            model=Book
            fields='__all__'
    
    class AddBookView(View):
    
        def get(self,request):
            form=BookModelForm
            return render(request,'add_book.html',locals())
    
        def post(self,request):
            form=BookModelForm(request.POST)
            if form.is_valid():
                form.save()
                return HttpResponse('OK')
            else:
                print(form.cleaned_data)
                print(form.errors)
    
            return HttpResponse('OK')
    
    class EditBookView(View):
    
        def get(self,request,id):
            edit_book=Book.objects.get(pk=id)
            form=BookModelForm(instance=edit_book)
            return render(request,'editbook.html',locals())
    
        def post(self,request,id):
            edit_book=Book.objects.get(pk=id)
            form=BookModelForm(request.POST,instance=edit_book)
            if form.is_valid():
                form.save()
                return HttpResponse('OK')
    
            else:
                print(form.cleaned_data)
                print(form.errors)
            return HttpResponse('OK')
    ModelForm
  • 相关阅读:
    如何在谷歌浏览器增加插件
    电脑更换硬盘
    电脑增加内存条
    了解计算机存储器
    Vue ----------- 了解, 展示json 数据
    JSON -------- json与字符串之间的转换
    JSON ------ 创建与访问
    Chartjs 简单使用 ------ 制作sin cos 折线图
    WebStorm ------------ 调整字体大小和背景
    输出正整数的各位数字
  • 原文地址:https://www.cnblogs.com/iamluoli/p/9035180.html
Copyright © 2011-2022 走看看