django之Form组件

Form组件   (什么是form组件?为什么要用rorm组件?怎么用form组件?)

  第一大功能:对用户请求的验证--->只要用户提交数据就可以用它来验证。

  第二大功能:生成HTML代码

前端发送请求,后台得到前端发送的内容,如果要存到数据库中需要对传过来的数据做验证,返回页面的时候,如果有错误需要在页面上展示出来。

要求:
    1.检查是否为空
    2.检查格式是否正确
比如: 对用户名的要求不能为空,长度在6
-8之间 对密码的要求不能为空,必须字母数字,长度必须大于8位 对邮箱的要求不能为空,必须是邮箱格式 对年龄的要求不能为空,必须是整数类型

我们要做的是

  模板层怎么提交数据

  视图层怎么获取数据,怎么校验数据

  校验完数据后

    1、我们需要将正确的如果写入数据库

    2、我们将验证时出现的错误信息在提交的页面上显示出来

Form组件校验字段功能

第一步:创建一个类。创建类必须引入和继承下面写的

from django import forms
class MyForm(forms.Form):

第二步:类中创建字段(包含正则表达式)。必须引入下面写的

from django import forms
from django.forms import fields
class MyForm(forms.Form):
    user = fields.CharField(max_length=20,min_length=1,required=True)

以上的意思是创建了一个类,写了一些字段,字段相应的设置了一些约束条件。最长18,最短1,不能为空。

用它定制的规则来验证用户发送的请求。把有用的信息提取出来才能进行操作。对应关系是:前端的input标签的name等于个什么就对应这里写的MyForm里面的字段名等于什么。比如前端在name是email的input标签里输入的值,就会与MyForm类的email字段里的规则进行比对。

models模型层

urls路由层

views视图层

  获取前端传过来的数据

  ORM操作

    写入数据库可以是一个字段一个字段对应的写,还可以传一个字典

templates模板层

  form表单和ajax都能向后端提交数据。

  区别:

    form提交是直接刷新页面,ajax是页面局部刷新。    

<!--
form表单提交数据,
    id是可以通过id值来找到这个form表单,然后进行相关操作
    action填写请求的url,
    method填请求的方式,
input标签,
    id是可以通过id值来找到这个input标签,然后进行相关操作
    type是input输入框输
    name填写的值就是后端获取input输入内容的键,用过它可以获取input提交的内容
-->
<form id="fm" action="/add_user/" method="post">
    {% csrf_token %}
    <div>
        <label for="name">用户名:</label>
        <input type="text" name="name" id="name">
    </div>
    <div>
        <label for="pwd">密码:</label>
        <input type="password" name="pwd" id="pwd">
    </div>
    <div>
        <label for="email">邮箱:</label>
        <input type="text" name="email" id="email">
    </div>
    <input type="submit" value="form提交">
    <input id="ajax_Btn" type="button" value="ajax提交">
</form>

<!--
ajax
    需要引入jquery文件
    需要给ajax提交按钮绑定事件让它去提交数据
    如何获取标签里的数据这里要用到JavaScript的知识
-->
<script src="/static/jquery.js"></script>
<script>
    $('#ajax_Btn').on('click',function () {
        $.ajax({
            url:'/add_user/',
            type:'post',
            //serialize()方法是获取
            data:$('#fm').serialize(),
            success:function (data) {
                //对后台返回的数据的具体操作
                console.log(data);
            },
        })
    })
</script>
form与ajax的最简单写法

检验字段功能的代码:

from django.shortcuts import render,redirect

from django import forms
from django.forms import fields
class MyForm(forms.Form):
    name = fields.CharField(max_length=20,min_length=1,required=True)
    pwd = fields.CharField(max_length=16,min_length=6,required=True)
    age = fields.IntegerField(required=True)
    email = fields.EmailField(required=True)
View Code

渲染错误信息功能

    <div>
        <label for="name">用户名:</label>
        <input type="text" name="name" id="name"><span>{{ errors_Msg.name }}</span>
    </div>
    <div>
        <label for="pwd">密码:</label>
        <input type="password" name="pwd" id="pwd">{{ errors_Msg.pwd }}
    </div>
    <div>
        <label for="age">年龄:</label>
        <input type="text" name="age" id="age">{{ errors_Msg.age }}
    </div>
    <div>
        <label for="email">邮箱:</label>
        <input type="email" name="email" id="email">{{ errors_Msg.email }}
    </div>
    <input type="submit" value="form提交">
{#    <input id="ajax_Btn" type="button" value="ajax提交">#}
</form>
html

出现这种情况是因为一个输入框可能同时出现两个错误。

解决方法就是我们只取错误提示的第一个。

    <div>
        <label for="name">用户名:</label>
        <input type="text" name="name" id="name"><span>{{ errors_Msg.name.0 }}</span>
    </div>
    <div>
        <label for="pwd">密码:</label>
        <input type="password" name="pwd" id="pwd">{{ errors_Msg.pwd.0 }}
    </div>
    <div>
        <label for="age">年龄:</label>
        <input type="text" name="age" id="age">{{ errors_Msg.age.0 }}
    </div>
    <div>
        <label for="email">邮箱:</label>
        <input type="email" name="email" id="email">{{ errors_Msg.email.0 }}
    </div>
    <input type="submit" value="form提交">
{#    <input id="ajax_Btn" type="button" value="ajax提交">#}
</form>
解决

如果改成中文

from django.shortcuts import render,redirect

from django import forms
from django.forms import fields

class MyForm(forms.Form):
    name = fields.CharField(
        max_length=20,
        min_length=1,
        required=True,
        error_messages={
            'required':'不能为空',
            'min_length':'太短了',
            'max_length':'太长了',
        }
    )
    pwd = fields.CharField(
        max_length=16,
        min_length=6,
        required=True,
        error_messages={
            'required': '不能为空',
            'min_length': '太短了',
            'max_length': '太长了',
        }
    )
    age = fields.IntegerField(
        required=True,
        error_messages={
            'required':'不能为空',
            # 所有遇到格式错误的都叫invalid
            'invalid':'格式错误'
        }
    )
    email = fields.EmailField(
        required=True,
        error_messages={
            'required':'不能为空',
            'invalid':'格式错误'
        }
    )
自定义校验的类

渲染标签(HTML)功能

协同开发,前端写HTML,后端写form。可能写的字段和前端的name属性对应不起来。怎么解决?

那就前端不要写了。在什么条件下生成HTML代码?

在GET请求时,创建一个MyForm的对象,这个对象可以点出来类里面的属性。所以把这个对象也传到前端。在前端可以这么写

def add_user(request):
    if request.method == 'GET':
        from_obj = MyForm()
        return render(request,'add_user.html',{'form_obj':from_obj})

在前端可以这么写

<form id="fm" action="/add_user/" method="post">
    {% csrf_token %}
    <div>
        {{ form_obj.name }}{{ errors_Msg.name.0 }}
    </div>
    <div>
        {{ form_obj.pwd }}{{ errors_Msg.pwd.0 }}
    </div>
    <div>
        {{ form_obj.age }}{{ errors_Msg.age.0 }}
    </div>
    <div>
        {{ form_obj.email }}{{ errors_Msg.email.0 }}
    </div>
    <input type="submit" value="form提交">
</form>

案例:

客户通过url来访问网站展示用户信息

通过添加按钮来添加用户,用form组件生成HTMLinput标签,get请求展示添加页面,并且进行字段校验,post请求如果前端填写的不正确要返回填写的内容以及错误信息。如果通过校验则写入数据库并且重新展示客户信息页面。

通过编辑操作来修改当前客户的信息,还是要求用form组件生成HTML标签,get请求时要根据用户的id获取当前用户的信息,然后到编辑页面并把用户信息在页面展示出来。如果是post请求还需要进行字段校验,校验成功更新,校验失败返回错误的提示。

<!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/css/bootstrap.min.css">
    <title>Title</title>
</head>
<body>
<a href="/add_user/">增加用户</a>
<div class="bs-example" data-example-id="bordered-table">
    <table class="table table-bordered table-hover">
        <thead>
        <tr>
            <th>id</th>
            <th>用户名</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
                <td>{{ user.id }}</td>
                <td>{{ user.username }}</td>
                <td>{{ user.email }}</td>
{#                <td><a href="/edit_user/?nid={{ user.id }}">编辑</a></td>#}
                <td><a href="/edit_user-{{ user.id }}/">编辑</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</div>
</body>
</html>
查看用户信息的HTML模板
<form id="fm" action="/add_user/" method="post" novalidate>
    {% csrf_token %}
    <div>
         <label for=""> 用户名</label>
        {{ form_obj.username }}{{ form_obj.errors.username.0 }}
    </div>
    <div>
         <label for=""> 邮箱</label>
        {{ form_obj.email }}{{ form_obj.errors.email.0 }}
    </div>
    <input type="submit" value="form提交">
</form>
添加用户的HTML
<!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>Title</title>
</head>
<body>
<form action="/edit_user-{{ nid }}/" method="post" novalidate>
    {% csrf_token %}
    <div>
         <label for=""> 用户名</label>
        {{ form_obj.username }}{{ form_obj.errors.username.0 }}
    </div>
    <div>
         <label for=""> 邮箱</label>
        {{ form_obj.email }}{{ form_obj.errors.email.0 }}
    </div>

    <input type="submit" value="提交">
</form>
</body>
</html>
编辑用户的HTML
from django import forms
from django.forms import fields


class MyForm(forms.Form):
    username = fields.CharField(
        max_length=20,
        min_length=2,
        required=True,
        error_messages={
            'max_length': '太长了',
            'min_length': '太短了',
            'required': '不能为空',
            'invalid': '格式不正确'
        })
    email = fields.EmailField(
        required=True,
        error_messages={
            'required': '必须填写邮箱',
            'invalid': '格式填写错误'
        }
    )
检验字段的form类
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users/', views.users),
    url(r'^add_user/', views.add_user),
    url(r'^edit_user-(d+)/', views.edit_user),
]
urls路由
from django.shortcuts import render,redirect
from app01 import models

from app01.myforms import MyForm


def users(request):
    if request.method == 'GET':
        user_list = models.UserInfo.objects.all()
        return render(request,'userInfo.html',{'user_list':user_list})
    elif request.method == 'POST':
        # return redirect('/get_userInfo/')
        return redirect('https://www.baidu.com')

def add_user(request):
    if request.method == 'GET':
        from_obj = MyForm()
        return render(request,'add_user.html',{'form_obj':from_obj})
    elif request.method == 'POST':
        # 数据与规则进行比对
        # 会自动去取前端的请求,比较完了之后产生一个结果,如果错误有错误信息,如果正确了会将正确数据给你。
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            models.UserInfo.objects.create(**form_obj.cleaned_data)
            return redirect('/users/')
        else:
            print('验证失败',form_obj.errors)
        return render(request,'add_user.html',{'form_obj':form_obj})



def edit_user(request,nid):
    if request.method == 'GET':
        # 从数据库拿数据到前端渲染
        user_data = models.UserInfo.objects.filter(id=nid).first()
        form_obj = MyForm({'username':user_data.username,'email':user_data.email})
        return render(request,'edit_user.html',{'form_obj':form_obj,'nid':nid})
    elif request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            models.UserInfo.objects.filter(id=nid).update(**form_obj.cleaned_data)
            return redirect('/users/')
        else:
            return render(request, 'edit_user.html',{'form_obj':form_obj,'nid':nid})
视图函数的逻辑代码

相关的mysql数据库配置、数据库表设计我们这里就不展示了。但是用form组件来生成HTML标签一定要注意对应关系,from的字段必须和input标签的对应。

form字段

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

我们自己写的类的字段它继承了Field:也就是django内置的字段

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则
 10     localize=False,              是否支持本地化
 11     disabled=False,              是否可以编辑
 12     label_suffix=None            Label内容后缀

如果字段是CharField格式里面常用的参数有:

  • required:True   不允许为空
  • max_length=18  最大长度为18
  • min_length=2    最小长度为2
  • strip:True         移除用户输入的空白

如果字段是IntegerField格式里面常用的参数

  • min_value=0     最小值
  • max_value=110  最大值
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
DateField(BaseTemporalField)    格式:2015-09-01
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
FileField(Field)
    allow_empty_file=False     是否允许空文件
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
     以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
         - view函数中 obj = MyForm(request.POST, request.FILES)
 ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
     required=True,             是否必填
     widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
     help_text='',              帮助提示

多选框

MultipleChoiceField(ChoiceField)

字符串转换成int类型

TypedChoiceField(ChoiceField)
     coerce = lambda val: val   对选中的值进行一次转换
     empty_value= ''            空值的默认值
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
   empty_value= ''            空值的默认值
GenericIPAddressField
     protocol='both',           both,ipv4,ipv6支持的IP格式
     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
from django import forms
from django.forms import fields
from django.forms import widgets


class UserForm(forms.Form):
    user = fields.CharField(
        max_length=20,
        min_length=2,
        required=True,
        label='用户名:',
        initial='请输入用户名',
        error_messages={
            'max_length': '太长了',
            'min_length': '太短了',
            'required': '不能为空',
        }
    )
    age = fields.IntegerField(
        required=True,
        min_value=0,
        max_value=110,
        label='年龄:',
        error_messages={
            'max_value': '年龄太大了',
            'min_value': '年龄太小了',
            'required': '不能为空',
        }
    )
    email = fields.EmailField(
        label='邮箱:',
        initial='请填写你的邮箱',
    )
    file = fields.FileField(
        label='文件:',

    )

    # ChoiceField生成下拉框
    city = fields.ChoiceField(
        label='城市:',
        choices=[(1, 'beijing'), (2, 'nanjing'), (3, 'shanghai')],
        initial=3,  # 默认选择3上海
    )

    hobby = fields.MultipleChoiceField(
        label='爱好',
        choices=[(1, 'read'), (2, 'music'), (3, 'traveling'), (4, 'swimming')],
        #设置默认值,列表
        initial=[1,2]
    )
自定义规则

  

form插件

插件的作用是自定义生成HTML标签,在每个插件里面可以给当前生成的这个标签自定义属性,属性叫attrs。

如果是对于特殊的下拉框对它的select或者是有这种数据源的东西的时候,我们用choices。

django内置的插件

 1 TextInput(Input)
 2 NumberInput(TextInput)
 3 EmailInput(TextInput)
 4 URLInput(TextInput)
 5 PasswordInput(TextInput)
 6 HiddenInput(TextInput)
 7 Textarea(Widget)
 8 DateInput(DateTimeBaseInput)
 9 DateTimeInput(DateTimeBaseInput)
10 TimeInput(DateTimeBaseInput)
11 CheckboxInput
12 Select
13 NullBooleanSelect
14 SelectMultiple
15 RadioSelect
16 CheckboxSelectMultiple
17 FileInput
18 ClearableFileInput
19 MultipleHiddenInput
20 SplitDateTimeWidget
21 SplitHiddenDateTimeWidget
22 SelectDateWidget

常用插件: 

# 单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
# )

数据源无法时时更新,有两种方法 

 方式一:重构构造方法(推荐)

方法一:重构构造方法(推荐)
    class ClassesForm(Form):
    name = fields.CharField(
        required=True,  # 必填字段
        error_messages={"required": "姓名不能为空!!"},  # 显示中文错误提示
        widget=widgets.TextInput(attrs={"placeholder": "姓名", "class": "form-control"}),  # 自动生成input框
    )
    # 如果直接定义成classteacher_id,,_id 的形式,这样你添加数据的时候不会时时更新,所以在下面定义一个重写的方法
    # classteacher_id = fields.ChoiceField(choices= models.UserInfo.objects.filter(ut_id = settings.ROLE_CLASSTEACHER).values_list('id', "username"))

        classteacher_id = fields.ChoiceField(choices=[])
        def __init__(self,*args,**kwargs):   #重写init方法,时时更新
            super().__init__(*args,**kwargs)   #继承父类
 
            self.fields["classteacher_id"].choices = models.UserInfo.objects.filter(ut_id = settings.ROLE_CLASSTEACHER).values_list('id', "username")
    注意:
        要是这样:fields.ChoiceField(choices=[])
        注意choices里面传[(1,"讲师"),(2,"班主任"),(3,"管理员")]所以数据库里取的时候得用values_list

 Form基本使用

类
    字段
    is_valid()
    cleaned_data
    errors
    字段参数:
        max_length
        min_length
        validators = [RegexValidators("xxx")]
        
    钩子函数
        clean_字段名
        注意:
            必须有返回值
            只能拿自己当前字段值
            raise ValidationError("xxx")
    下拉框数据源时时更新
        1、重写init方法
            先执行父类构造方法
            self.fields["xx"].choices = xxxxx
        2、ModelChoiceField

end

原文地址:https://www.cnblogs.com/zhangrenguo/p/10293779.html