Python之路--Django--form组件与model form组件

1、form组件介绍

咱们以前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签而且用form标签把它们包起来。html

与此同时咱们在好多场景下都须要对用户的输入作校验,好比校验用户是否输入,输入的长度和格式等正不正确。若是用户输入的内容有错误就须要在页面上相应的位置显示对应的错误信息.。前端

Django form组件就实现了上面所述的功能。git

总结一下,其实form组件的主要功能以下:正则表达式

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

 

一、普通方式手写注册功能

views.py数据库

# 注册
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})

login.htmldjango

<!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组件实现注册功能

views.py

先定义好一个RegForm类:bootstrap

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})

login2.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" value="注册">
        </div>
    </form>
</body>
</html>

看网页效果发现 也验证了form的功能:
• 前端页面是form类的对象生成的                                      -->生成HTML标签功能
• 当用户名和密码输入为空或输错以后 页面都会提示             -->用户提交校验功能
• 当用户输错以后 再次输入 上次的内容还保留在input框        -->保留上次输入内容
app

 

三、经常使用字段与插件

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

initial:

初始值,imput框里面的初始值

class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三"  # 设置默认值
 ) pwd = forms.CharField(min_length=6, label="密码")

 

error_messages

自定义错误信息

class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码")

 

password

class LoginForm(forms.Form): ... pwd = forms.CharField( min_length=6, label="密码", widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )

 

radioSelect

单radio值为字符串

class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码") gender = forms.fields.ChoiceField( choices=((1, ""), (2, ""), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() )

 

单选Select

class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() )

 

多选Select

class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )

 

单选checkbox

class LoginForm(forms.Form): ... keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )

 

多选checkbox

class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )

关于choice的注意事项:

在使用选择标签时,须要注意choices的选项能够从数据库中获取,可是因为是静态字段 ***获取的值没法实时更新***,那么须要自定义构造方法从而达到此目的。

方式一:

from django.forms import Form from django.forms import widgets from django.forms import fields class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),),
        initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) # self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
        #
        self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')

方式二:

from django import forms from django.forms import fields from django.forms import models as form_model class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多选
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 单选

 

四、Django Form全部内置字段

Field required=True, 是否容许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'} 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类型

 

五、校验

方式一:

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类的init方法来实现。

class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ... def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' }) 批量添加样式

 

2、Django的model form组件

  这是一个神奇的组件,经过名字咱们能够看出来,这个组件的功能就是把model和form组合起来,

  先来一个简单的例子来看一下这个东西怎么用:好比咱们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,如今让你写一个建立学生的页面,你的后台应该怎么写呢?首先咱们会在前端一个一个罗列出这些字段,让用户去填写,而后咱们从后台一个一个接收用户的输入,建立一个新的学生对象,保存其实,重点不是这些,而是合法性验证,咱们须要在前端判断用户输入是否合法,好比姓名必须在多少字符之内,电话号码必须是多少位的数字,邮箱必须是邮箱的格式这些固然能够一点一点手动写限制,各类判断,这毫无问题,除了麻烦咱们如今有个更优雅(之后在Python相关的内容里,要多用“优雅”这个词,而且养成习惯)的方法:ModelForm先来简单的,生硬的把它用上,再来加验证条件。

 

一、建立modelform

#首先导入ModelForm

from django.forms import ModelForm #在视图函数中,定义一个类,好比就叫StudentList,这个类要继承ModelForm,在这个类中再写一个原类Meta(规定写法,并注意首字母是大写的) #在这个原类中,有如下属性(部分):

class StudentList(ModelForm): class Meta: model =Student #对应的Model中的类
        fields = "__all__" #字段,若是是__all__,就是表示列出全部的字段
        exclude = None #排除的字段
        #error_messages用法:
        error_messages = { 'name':{'required':"用户名不能为空",}, 'age':{'required':"年龄不能为空",}, } #widgets用法,好比把输入用户名的input框给为Textarea
        #首先得导入模块
        from django.forms import widgets as wid #由于重名,因此起个别名
        widgets = { "name":wid.Textarea(attrs={"class":"c1"}) #还能够自定义属性
 } #labels,自定义在前端显示的名字
        labels= { "name":"用户名" }

而后在url对应的视图函数中实例化这个类,把这个对象传给前端

def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list}) 

  而后前端只须要 {{ student_list.as_p }} 一下,全部的字段就都出来了,能够用as_p显示所有,也能够经过for循环这student_list,拿到的是一个个input框,如今咱们就不用as_p,手动把这些input框搞出来,as_p拿到的页面太丑。

  首先 for循环这个student_list,拿到student对象,直接在前端打印这个student,是个input框student.label ,拿到数据库中每一个字段的verbose_name ,若是没有设置这个属性,拿到的默认就是字段名,还能够经过student.errors.0 拿到错误信息有了这些,咱们就能够经过bootstrap,本身拼出来想要的样式了,好比:

<body>
<div class="container">
    <h1>student</h1>
    <form method="POST" novalidate> {% csrf_token %}
{# {{ student_list.as_p }}#}
{% for student in student_list %}
<div class="form-group col-md-6"> {# 拿到数据字段的verbose_name,没有就默认显示字段名 #} <label class="col-md-3 control-label">{{ student.label }}</label> <div class="col-md-9" style="position: relative;">{{ student }}</div> </div> {% endfor %} <div class="col-md-2 col-md-offset-10"> <input type="submit" value="提交" class="btn-primary"> </div> </form> </div> </body>

如今还缺一个input框的form-contral样式,能够考虑在后台的widget里面添加,好比这样:

from django.forms import widgets as wid #由于重名,因此起个别名
widgets = { "name":wid.TextInput(attrs={'class':'form-control'}), "age":wid.NumberInput(attrs={'class':'form-control'}), "email":wid.EmailInput(attrs={'class':'form-control'}) }

固然也能够在js中,找到全部的input框,加上这个样式,也行。

 

二、添加纪录

保存数据的时候,不用挨个取数据了,只须要save一下

def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list}) else: student_list = StudentList(request.POST) if student_list.is_valid(): student_list.save() return redirect(request,'student_list.html',{'student_list':student_list})

 

三、编辑数据

  若是不用ModelForm,编辑的时候得显示以前的数据吧,还得挨个取一遍值,若是ModelForm,只须要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就能够获得一样的效果
保存的时候要注意,必定要注意有这个对象(instance=obj),不然不知道更新哪个数据
代码示例:

from django.shortcuts import render,HttpResponse,redirect from django.forms import ModelForm # Create your views here.
from app01 import models def test(request): # model_form = models.Student
    model_form = models.Student.objects.all() return render(request,'test.html',{'model_form':model_form}) class StudentList(ModelForm): class Meta: model = models.Student #对应的Model中的类
        fields = "__all__" #字段,若是是__all__,就是表示列出全部的字段
        exclude = None #排除的字段
        labels = None #提示信息
        help_texts = None #帮助提示信息
        widgets = None #自定义插件
        error_messages = None #自定义错误信息
        #error_messages用法:
        error_messages = { 'name':{'required':"用户名不能为空",}, 'age':{'required':"年龄不能为空",}, } #widgets用法,好比把输入用户名的input框给为Textarea
        #首先得导入模块
        from django.forms import widgets as wid #由于重名,因此起个别名
        widgets = { "name":wid.Textarea } #labels,自定义在前端显示的名字
        labels= { "name":"用户名" } def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list}) else: student_list = StudentList(request.POST) if student_list.is_valid(): student_list.save() return render(request,'student.html',{'student_list':student_list}) def student_edit(request,pk): obj = models.Student.objects.filter(pk=pk).first() if not obj: return redirect('test') if request.method == "GET": student_list = StudentList(instance=obj) return render(request,'student_edit.html',{'student_list':student_list}) else: student_list = StudentList(request.POST,instance=obj) if student_list.is_valid(): student_list.save() return render(request,'student_edit.html',{'student_list':student_list})

  总结: 从上边能够看到ModelForm用起来是很是方便的,好比增长修改之类的操做。可是也带来额外很差的地方,model和form之间耦合了。若是不耦合的话,mf.save()方法也没法直接提交保存。 可是耦合的话使用场景一般局限用于小程序,写大程序就最好不用了。

相关文章
相关标签/搜索