一篇文章搞懂Django中的form表单
liuian 2024-12-11 15:45 87 浏览
Form介绍
之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
普通的登录
views.py
def login(request):
error_msg = ""
if request.method == "POST":
username = request.POST.get("username")
pwd = request.POST.get("pwd")
if username == "SKS" and pwd == "1366768":
return HttpResponse("OK")
else:
error_msg = "用户名或密码错误"
return render(request, "login.html", {"error_msg": error_msg})login.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>login</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<form action="/login/" method="post">
{% csrf_token %}
<p>
<label for="username">用户名</label>
<input type="text" name="username" id="username">
</p>
<p>
<label for="pwd">密码</label>
<input type="password" name="pwd" id="pwd">
<span class="error"></span>
</p>
<p>
<input type="submit">
<span class="error">{{ error_msg }}</span>
</p>
</form>
</body>
</html>使用form组件
views.py
先定义好一个LoginForm类。
# 定义一个form组件类
class LoginForm(forms.Form):
# 验证的字段及条件
username = forms.CharField(min_length=10, label="用户名")
pwd = forms.CharField(min_length=10, label="密码")
def login(request):
# 存储错误信息
error_msg = ""
# 实例化对象
form_obj = LoginForm()
# 判断前端页面请求是否是POST请求
if request.method == "POST":
# 将数据传入form组件类中
form_obj = LoginForm(request.POST)
# 存储的正确信息
if form_obj.is_valid():
username = form_obj.cleaned_data.get("username")
pwd = form_obj.cleaned_data.get("pwd")
if username == "SKS" and pwd == "1866768":
return HttpResponse("OK")
else:
error_msg = "用户名或密码错误"
return render(request, "login2.html", {"form_obj": form_obj, "error_msg": error_msg})login2.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>login</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<form action="/login2/" method="post" novalidate>
{% csrf_token %}
<p>
{{ form_obj.username.label }}
{{ form_obj.username }}
<span class="error">{{ form_obj.username.errors.0 }}</span>
</p>
<p>
{{ form_obj.pwd.label }}
{{ form_obj.pwd }}
<span class="error">{{ form_obj.pwd.errors.0 }}</span>
</p>
<p>
<input type="submit">
<span class="error">{{ error_msg }}</span>
</p>
</form>
</body>
</html>看网页效果发现 也验证了form的功能:
- 前端页面是form类的对象生成的 -->生成HTML标签功能
- 当用户名和密码输入为空或输错之后 页面都会提示 -->用户提交校验功能
- 当用户输错之后 再次输入 上次的内容还保留在input框 -->保留上次输入内容
Form组件
常用字段演示
initial
初始值,input框里面的初始值。
# 定义一个类继承forms.Form
class LoginForm(forms.Form):
username = forms.CharField(
min_length=10,
label="用户名",
initial="hewm" # 设置默认值方法
)
pwd = forms.CharField(min_length=10, label="密码")error_messages
重写错误信息。
class LoginForm(forms.Form):
username = forms.CharField(
min_length=10,
label="用户名",
initial="rum",
# 重写错误提示信息
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短10位"
}
)
pwd = forms.CharField(min_length=10, label="密码")password
class LoginForm(forms.Form):
...
pwd = forms.CharField(
min_length=10,
label="密码",
# 密文方法 参数attrs:样式类 render_value=验证失败是否回填widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )radioSelect
单radio值为字符串
class LoginForm(forms.Form):
username = forms.CharField(
min_length=10,
label="用户名",
initial="rum",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短10位"
}
)
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
)
# 初始化init方法 (执行时间为类实例化时执行)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['user'].widget.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())内置字段
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类型
Django form内置字段校验
方式一:
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]+#39;, '请输入数字'), RegexValidator(r'^159[0-9]+#39;, '数字必须以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}#39;)
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'邮箱'}))补充进阶
应用Bootstrap样式
<!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>login</title>
</head>
<body>
<div class="container">
<div class="row">
<form action="/login2/" method="post" novalidate class="form-horizontal">
{% csrf_token %}
<div class="form-group">
<label for="{{ form_obj.username.id_for_label }}"
class="col-md-2 control-label">{{ form_obj.username.label }}</label>
<div class="col-md-10">
{{ form_obj.username }}
<span class="help-block">{{ form_obj.username.errors.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{ form_obj.pwd.id_for_label }}" class="col-md-2 control-label">{{ form_obj.pwd.label }}</label>
<div class="col-md-10">
{{ form_obj.pwd }}
<span class="help-block">{{ form_obj.pwd.errors.0 }}</span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">{{ form_obj.gender.label }}</label>
<div class="col-md-10">
<div class="radio">
{% for radio in form_obj.gender %}
<label for="{{ radio.id_for_label }}">
{{ radio.tag }}{{ radio.choice_label }}
</label>
{% endfor %}
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">注册</button>
</div>
</div>
</form>
</div>
</div>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>批量添加样式
可通过重写form类的init方法来实现。
class LoginForm(forms.Form):
username = forms.CharField(
min_length=10,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短10位"
}
...
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'
})源码简易剖析
views.py文件
def register(request):
if request.method == "POST":
res = {"user": None, "error_dict": None}
form = RegForm(request.POST)
# 存储验证通过的信息.is_valid()(源码剖析开始点)
if form.is_valid():
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar = request.FILES.get("avatar")
print(user,pwd,email,avatar)
if avatar:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar)
else:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email)
res["user"] = user.username
else:
print(form.errors)
res["error_dict"] = form.errors
return JsonResponse(res)
form = RegForm()
return render(request, 'register.html', locals())点击进入forms.py文件
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
# 含义:返回布尔值,只要有数据并且没有错误信息就返回True
return self.is_bound and not self.errors点击self.errors进入forms.py文件中
# 静态方法
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
# 判断self._errors是否为空
if self._errors is None:
self.full_clean()
return self._errors点击._errors进入forms.py文件中
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
self.is_bound = data is not None or files is not None
self.data = data or {}
self.files = files or {}
self.auto_id = auto_id
if prefix is not None:
self.prefix = prefix
self.initial = initial or {}
self.error_class = error_class
# Translators: This is the default suffix added to form field labels
self.label_suffix = label_suffix if label_suffix is not None else _(':')
self.empty_permitted = empty_permitted
# 默认值为None
self._errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
# fields. Because a particular *instance* of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = copy.deepcopy(self.base_fields)
self._bound_fields_cache = {}
self.order_fields(self.field_order if field_order is None else field_order)返回def errors(self)
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean() # 这个方法才是真正帮忙执行效验操作
return self._errors点击full_clean方法
def full_clean(self):
"""
Cleans all of self.data and populates self._errors and
self.cleaned_data.
"""
self._errors = ErrorDict() # 定义一个保存错误信息字典
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()点击self._clean_fields()进入
def _clean_fields(self):
for name, field in self.fields.items():
# self.fields:类似一个字典
#for name, field in self.fields.items():解释分别获取self.fields的键和值分别赋值给 name, field
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField): # 如果是文件字段
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else: # field.clean(value) 之后Dbug运行检查,为循环判断错误
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)#科技##python##一起学python##Python 语言##Python入门推荐#
相关推荐
- 桌面启动命令(电脑桌面启动项命令)
-
方法一打开C:\Windows\System32目录,找到cmd.exe,单击选中后右键,菜单中选择“以管理员身份运行”。方法二1、点开开始菜单,在搜索框中输入“cmd”,在搜索结果中,对着命令...
- miui下载miui官网手机版(小米miui下载官网手机版)
-
1.打开手机浏览器,输入miui官网地址。2.点击下载页面,选择你手机的型号。3.点击下载,下载完成后双击安装。4.安装完成后,重新启动手机。小米官网下载地址是:http://www.mi.com/s...
- win10任务管理器被禁用怎么解除
-
1.首先,检查是否有管理员权限,只有管理员有权限禁用任务管理器;2.查看任务管理器是否被禁用,可以在注册表中查看,路径为"HKEY_CURRENT_USER\Software\Micros...
- flash player有必要安装吗(flash播放器有必要安装吗)
-
现代的电脑不再需要安装AdobeFlashPlayer,因为许多主流浏览器已经停止对Flash的支持。FlashPlayer是用于播放动画、视频和音频等多媒体内容的插件,但由于安全性问题和技术发...
- cmlink欧洲(欧洲联通)
-
cmlink是一种用于创建和管理内容链接的工具。它可以帮助用户将不同的内容链接在一起,形成一个完整的内容体系,方便用户查看和使用。cmlink可以将多个不同的内容链接整合在一起,形成一个自定义的内容库...
- window7截图快捷键(winds7截图快捷键)
-
win7的截图快捷键如下:1、按Prtsc键截图 这样获取的是整个电脑屏幕的内容,按Prtsc键后,可以直接打开画图工具,接粘贴使用。也可以粘贴在QQ聊天框或者Word文档中,之后再选择保存即可...
-
- 万能网卡驱动下载离线版(万能网卡驱动离线版pc win10)
-
使用驱动精灵在没有网络的情况下安装驱动,需要安装驱动精灵万能网卡版。安装驱动精灵万能网卡版步骤如下所示:1、点击搜索结果词条,进入驱动精灵官网。2、在驱动精灵官网,选择驱动精灵万能网卡版,点击下载。3、下载完成后,点击安装驱动精灵万能网卡版...
-
2025-12-20 16:05 liuian
- win10 1803版本(window10版本1803)
-
Windows10version1803版本,也就是“April2018Update”。Win101803正式版版本号为Build17134,资料显示,Build17134即Vers...
- 资源管理器停止黑屏怎么恢复
-
作为临时对策,重启“explorer.exe”即可,步骤如下:ctrl+alt+delete,调出任务管理器;选择“进程”,找到“explorer.exe”,结束进程;选择“文件”,之后新建“expl...
- win10企业版激活密钥大全(windows10企业版激活密钥在哪)
-
详细版本号:点击开始——设置——系统——关于。查看密钥:右键点击开始——运行,输入regedit确定。打开注册表编辑器。依次打开:HKEY_LOCAL_MACHINE/SOFTWARE/Micro...
- 路由器基本设置(路由器基本设置方法)
-
如何设置路由器?设置路由器并不是很复杂的事情,并且需要设置地方也很少,一般保持默认即可;并且在设置方式上,也不一定使用电脑设置,任何移动终端接入路由器都可以完成设置。一起来看看,究竟该如何设置路由器吧...
- 迅雷破解版安卓(迅雷破解版安卓版6.0)
-
去其他电影网站下载屏蔽的那些网站比较正规,现在越来越注重版权了虽然迅雷5.0和皮皮播放器,PPlive中可以修改SP3最大连接数,但是只能对其软件本身有效,而无法对系统和其他软件的最大连接数进行修改...
- win7简约主题(windows7主题)
-
要设置小米手机的全局主题为简约风格,首先进入手机的主题设置界面,选择简约风格的主题并进行下载和安装。接着在桌面设置中调整图标样式、字体和壁纸等元素为简约风格。还可以在系统设置中调整通知栏、锁屏和系统界...
- win在键盘上是哪个键(键盘上的win键在哪儿)
-
在大多数计算机键盘上,Win键是指Windows键,它通常位于键盘的底部,靠近空格键和Ctrl键之间。Win键上通常有Windows徽标,它是微软Windows操作系统的标志。通过按下Win键,可以打...
-
- 电脑找不到无线网络连接(电脑找不到无线网络连接图标)
-
1、进入系统按win+i打开Windows设置,点击“个性化”。2、点击“任务栏”-“打开或关闭系统图标”。3、我们可以查看到“网络”开关被关闭了,点击开关将其打开即可。5、返回桌面可以看到任务栏右侧就会显示网络图标了,点击wifi图标即可...
-
2025-12-20 12:05 liuian
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
