Django中的Form & ModelForm

在web开发中,处理表单是家常便饭,我们当然可以直接在模板中编写原生的html表单,然后在后端进行表单验证,这常常是一项繁琐的工作。

Form

我们可以通过Django提供了表单功能,来方便表单的处理。下面看一下如何使用:

  1. 自定义表单类

    from django import forms  # 导入表单模块
    from django.core.exceptions import ValidationError class RegisterForm(forms.Form): # 自定义表单类,并继承forms.Formemail = forms.EmailField(widget=forms.EmailInput( attrs={"class": "form-control"}))username = forms.CharField(min_length=4, max_length=12, widget=forms.TextInput(attrs={"class": "form-control"}))password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={"class": "form-control"}))password2 = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={"class": "form-control"}))valid_code = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))def __init__(self, request, *args, **kwargs):# 如果需要额外接收参数,要重写构造器函数# 这里额外接收一个参数,用于从request.sesssion中提取之前保存的验证码super(RegisterForm,self).__init__(*args, **kwargs) self.request = request# 自定义方法(局部钩子),密码必须包含字母和数字def clean_password(self):if self.cleaned_data.get('password').isdigit() or self.cleaned_data.get('password').isalpha():raise ValidationError('密码必须包含数字和字母')else:return self.cleaned_data['password']def clean_valid_code(self):  # 检验验证码正确;之前生成的验证码保存在了了session中if self.cleaned_data.get('valid_code').upper() == self.request.session.get('valid_code'):return self.cleaned_data['valid_code']else:raise ValidationError('验证码不正确')# 自定义方法(全局钩子, 检验两个字段),检验两次密码一致;def clean(self):if self.cleaned_data.get('password') != self.cleaned_data.get('password2'):raise ValidationError('密码不一致')else:return self.cleaned_data# 注意,上面的字典取值用get, 因为假如在clean_password中判断失败,那么没有返回值,最下面的clean方法直接取值就会失败

    说明:

    1. min_length=4, max_length=12定义字段字符长度
    2. error_messages={"required":["请输入有效的......"]} 自定义错误信息
    3. widget参数用来指定字段的类型和属性;
    4. clean是验证函数,分为局部钩子和全局钩子;局部钩子clean_field用于验证单个字段,全局钩子clean用于验证两个字段;
    5. clean验证函数必须有返回值;
  2. 实例化表单对象,传入模板渲染

    
    # 实例化表单对象; 因为重写了init方法,这里要额外接收requestform_obj = RegisterForm(request) 
    <form action="{% url 'register' %}" method="post" id="register">{% csrf_token %}<div class="form-group"><label for="id_email">邮箱&nbsp;</label><span class="error-info"></span>{{ form_obj.email }}
    <!--  span 标签用于放置错误信息 --></div>......<p class="reg-button"><button type="submit" class="btn btn-primary btn-block">注册</button></p></form>

    说明:

    <form>{{form_obj.as_p}}</form>也可以通过as_p方法直接生成整个表单,但是自定义性比较差。

  3. 验证提交的表单

    
    # 传入request.POST,实例化表单对象用于户验证和提取数据form_obj = RegisterForm(request, request.POST) # 方法:form_obj.is_valid()  # 根据字段属性和验证函数进行校验
    form.cleaned_data.get(field)  # 校验OK,提取数据
    form.errors  # 错误信息:{field:[error_info], field:[],...} 
  4. 根据用户提交,保存或修改数据库

    略……

ModelForm

用过Django Form功能的可能会觉得用起来很繁琐,要写一堆验证方法,验证通过后还是要一一提取数据,最终的操作都是数据库模型中进行保存或修改操作。不难发现Form表单的字段和对应的数据库Model几乎是一样的,于是Django提供了ModelForm,极大的简化了表单的处理和验证。我们看下如何使用。

  1. 自定义表单,继承ModelForm

    from django.forms import ModelForm # 导入ModelForm
    from django.forms import fields
    from .models import UserInfo, Role, Permission, Menu # 导入Modelclass UserInfoModelForm(ModelForm):username = fields.CharField(required=True)"""可以在这里单独定义字段的具体类型和属性,同Form如果定义的字段在model中存在,则会覆盖;"""class Meta:model = UserInfofields = '__all__'labels = {'username': '用户名','password': '密码','nickname': '昵称','email': '邮箱',}

    说明:

    1. model: 对应的数据库model

    2. fields: 选择字段列表;’all’是选择所有字段

    3. exclude: 排除字段列表

    4. widgets: 插件字典

    5. labels: 前端显示字段名

    6. error_messages: 自定义错误提示

    7. localized_fields: 本地化,如:根据不同时区显示数据

      数据库中2016-12-27 04:10:57
      setting中的配置TIME_ZONE = 'Asia/Shanghai'USE_TZ = True
      则显示:2016-12-27 12:10:57

  2. 实例化表单对象,传入模板,同Form

    model_form = UserInfoModelForm()
  3. 验证提交的表单

    model_form = UserInfoModelForm(request.POST)
    if model_form.is_valid():model_form.save()

    is_valid()方法会根据model字段的类型以及自定义验证方法来验证提交的数据;如果is_valid()验证通过,直接save(),就可以完成向数据库中插入一条记录。

  4. 如果是修改记录,需要传入记录对象

    
    # 实例化表单对象,传入模板model_form = UserInfoModelForm(instance=user_obj)# 验证提交model_form = UserInfoModelForm(request.POST, instance=user_obj)
    if model_form.is_valid():model_form.save()

ChoiceField字段

用于外键字段

例1:将外键字段定义为ChoiceField字段,这里以Form表单为例来说明:

class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用户名不能为空'})password = fields.CharField(required=True, error_messages={'required': '密码不能为空'})email = fields.EmailField(required=True)# depart为外键字段,那么我们可以将其变为下拉选项(非外键字段也可以,只要给它提供[(m, n), (m, n)]这种结构的数据源就可以)depart = fields.ChoiceField(choices=models.Department.objects.values_list('id', 'title'))

depart字段的模板渲染结果:

<select name="depart" id="id_depart"><option value="1">销售</option><option value="2">公关</option><option value="3">技术</option><option value="4">后勤</option>
</select>

用于自定义字段

例2:这里以ModelForm为例来进行说明:

def func():return [(1, '北京'),(2, '上海'),(3, '武汉'),]class UserInfoModelForm(ModelForm):city = fields.ChoiceField(choices=func())"""可以在这里单独定义字段的具体类型和属性,同Form如果定义的字段在model中存在,则会覆盖;假设,model中的city字段是CharField类型,默认用ModelForm会生成input框,但是我们又不希望每次都手动填入数据。那么,就可以单独定义该字段为ChoiceField类型。只需要为其提供[(m, n), (m, n)]这种结构的数据源就可以,比如这里选择通过一个函数来实现。"""class Meta:model = UserInfofields = '__all__'labels = {'username': '用户名','password': '密码','email': '邮箱',}

city字段的模板渲染结果:

<select name="city" id="id_city"><option value="1">北京</option><option value="2">上海</option><option value="3">武汉</option>
</select>

用于静态字段

以上两个栗子,分别用了外键的关联表和函数返回值作为表单中ChoiceField字段的下拉选项数据。在实际使用中,常常在model中定义一个静态字段,来作为数据来源,如下:

from django.db import modelsclass Department(models.Model):passclass UserInfo(models.Model):city_choices = [(1, '北京'),(2, '上海'),(3, '武汉'),]username = models.CharField(verbose_name='用户名', max_length=32, null=True, blank=True)password = models.CharField(verbose_name='密码', max_length=64, null=True, blank=True)email = models.EmailField(verbose_name='邮箱', max_length=64, null=True, blank=True)# city以静态字段city_choices作为数据来源city = models.IntegerField(verbose_name='城市', choices=city_choices, null=True, blank=True)depart = models.ForeignKey(verbose_name='部门', to="Department", null=True, blank=True)

bug

不论数据是来自静态字段还是外键关联表,ChoiceField字段的值都是是从数据库获取的。但是一旦数据源更新了,模板在渲染时无法显示最新的数据;除非重启程序。这是因为,定义了Form或ModelForm后,ChoiceField会从数据库取一次值,后面再使用时这个值就不更新了。因此,要动态生成ChoiceField字段的数据。

解决办法:不在定义阶段获取数据,在每次实例化表单时获取数据,重写__init__方法:

class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用户名不能为空'})password = fields.CharField(required=True, error_messages={'required': '密码不能为空'})email = fields.EmailField(required=True)# depart这里只定义字段类型depart = fields.ChoiceField()def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 实例化时才动态获取数据self.fields['depart'].choices = models.Department.objects.values_list('id', 'title')

除了上面这种手动的方式,我们也可以利用ModelChoiceField字段,达到动态获取数据的目的:

class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用户名不能为空'})password = fields.CharField(required=True, error_messages={'required': '密码不能为空'})email = fields.EmailField(required=True)# ModelChoiceField字段动态获取数据depart = ModelChoiceField(queryset=models.Department.objects.all())

depart模板渲染结果:

<select name="depart" required="" id="id_depart"><option value="" selected="">---------</option><option value="1">销售</option><option value="2">公关</option><option value="3">技术</option><option value="4">后勤</option>
</select>

不过这里还是推荐通过第一种,重写__init__的方式,定制性更强;

第二种ModelChoiceField自动获取数据,它接收QuerySet对象,因此,如果没有在model中定义__str__方法,渲染出来的结果将是这样子的。。。

<select name="depart" required="" id="id_depart"><option value="" selected="">---------</option><option value="1">Department object</option><!-- 没有可读性。。。 -->
</select>

Django中的Form ModelForm相关推荐

  1. Django中的Form

    2019独角兽企业重金招聘Python工程师标准>>> 一.使用Form Django中的Form使用时一般有两种功能: 1.生成html标签 2.验证输入内容 要想使用django ...

  2. Django中的form如何设置field的html属性呢?

    在Django中无论何种field,都有一个widget的属性: 1 class Field(object): 2 widget = TextInput # Default widget to use ...

  3. django中的form.is_valid()总是返回False

    这个问题是由于html模板中提交的参数名字和forms.py中的变量名字不一致导致的: 下面是正确示范: templates/pwdreset.html <h3 class="no-m ...

  4. Django中的form模块的高级处理

    forms高级处理 使用原理:将Models和Forms结合到一起使用,将Forms中的类和Models中的类关联到一起,实现属性的共享 使用步骤: 在forms.py中创建class 继承自form ...

  5. django框架之form组件

    内容回顾: 1. 内容回顾     1. 复习JSON         1. JSON是什么?             一种数据格式,和语言无关的数据格式.         2. Python里面转换 ...

  6. 3 django系列之Form表单在前端web界面渲染与入库保存

    author: 温柔易淡(Leo),欢迎技术交流与拍砖 preface 我们在前端写表单的时候,其实可以使用django自带的forms功能来实现,特别是在处理 修改已经存在数据 的场景特别好用,下面 ...

  7. django一个html先后两个form,Django教程(三)- Django表单Form

    目录: 1.Form 基本使用 django中的Form组件有以下几个功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 2.Form中 ...

  8. Django基础11(Django中form表单)

    Form介绍 之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入 ...

  9. Django中Form组件的使用

    Form介绍 HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入 ...

最新文章

  1. 学术分享丨面向机器人的学习算法简述
  2. 分子计算机已经问世,纳米计算机指日可待?
  3. 大话设计模式—命令模式
  4. java 独占锁_锁分类(独占锁、分拆锁、分离锁、分布式锁)
  5. oracle和mysql通用建表语句_mysql建表语句到oracle怎么写?
  6. windows下常用DOS命令
  7. 尚硅谷设计模式笔记-适配器模式
  8. Swift 编程语言
  9. Linux下7z工具安装
  10. Android Studio 的蓝牙串口通信(附Demo源码下载)
  11. matlab计算并联电阻怎么输入,如何用计算器快速计算并联电阻,并联电阻的计算方法...
  12. 层级结构——组合模式
  13. 花生壳内网穿透操作文档
  14. struct.error: short format requires (-32768) <= number <= 32767
  15. go老鸟也会经常出现 并发channal问题引起的协程泄漏
  16. 表格中自动布局注意和使用自动计算行高
  17. excel 查找图相框或文本框中的内容
  18. 【GameMaker 极速入门】#1环境配置
  19. 官方免费的正版Xshell,人人都可以马上拥有
  20. 《动态规划》— 动态规划分类

热门文章

  1. 4-4:TCP协议之TCP头部格式详解
  2. Linux Dynamic Shared Library LD Linker
  3. python 中del 的用法
  4. linux syslog日志
  5. 56 - I. 数组中数字出现的次数
  6. linux系统调用sysconf(获取系统信息)
  7. SQLAlchemy 教程 —— 基础入门篇
  8. 通过socket得到远端的IP和连接端口
  9. java课程之团队开发冲刺阶段1.7
  10. OLW (Open Live Writer)安装代码高亮插件方法(简明)