form组件
先来个注册页面项目
models.py
# models.py
from django.db import models
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.CharField(max_length=32, unique=True, null=False)
pwd = models.CharField(max_length=10)
email = models.EmailField(default="aki@gmail.com")
mobile = models.CharField(max_length=11)
class Meta:
unique_together = ('user',)
def __str__(self):
return self.user
views.py
含有自定义校验字段
# views.py
from django.shortcuts import render, HttpResponse
from app0001 import models
from django import forms
from django.forms import widgets, fields
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class RegForm(forms.Form):
user = forms.CharField(max_length=8, min_length=2, label="用户名",
error_messages={
"min_length": "用户名不少于2位",
"required": "该字段不能为空",
},
widget=widgets.TextInput(attrs={"class": "form-control"}))
pwd = forms.CharField(max_length=10, min_length=4, label="密码",
error_messages={
"min_length": "密码不少于4位",
"required": "该字段不能为空",
},
widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True)
# render_value 刷新页面密码不丢失, 只有密码有这个属性
)
re_pwd = forms.CharField(max_length=10, min_length=4, label="确认密码",
error_messages={
"min_length": "密码不少于4位",
"max_length": "密码太长",
"required": "该字段不能为空",
},
widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True)
)
email = forms.EmailField(label="邮箱",
widget=widgets.EmailInput(attrs={"class": "form-control"}),
disabled=True,
# 不可编辑
initial='jjssw@qq.com',
# 指定默认值
error_messages={
"required": "该字段不为空",
"invalid": "格式错误",
})
mobile = forms.CharField(label="手机号",
widget=widgets.TextInput(attrs={"class": "form-control"}),
validators=[
RegexValidator(r'^[0-9]+$', '输入数字'),
RegexValidator(r'^1[3-9][0-9]{9}$', '格式错误')
],
error_messages={
"required": "该字段不为空",
})
def clean_user(self):
value = self.cleaned_data.get('user')
a = ["敏感", "词汇"]
for i in a:
if i in value:
raise ValidationError('形式所迫')
is_exist = models.UserInfo.objects.filter(user=value)
if is_exist:
raise ValidationError('用户名重了, 老弟')
return value
def clean(self):
# 注释掉的是检查用户名名是否存在, 而且局部钩子先生效, 也就是说先走局部钩子
# user = self.cleaned_data.get('user')
# is_exist = models.UserInfo.objects.filter(user=user)
pwd = self.cleaned_data.get("pwd")
re_pwd = self.cleaned_data.get("re_pwd")
if pwd != re_pwd:
self.add_error("re_pwd", ValidationError("密码不一致"))
raise ValidationError('')
# if is_exist:
# self.add_error('user', ValidationError("用户名重复"))
# raise ValidationError('')
return self.cleaned_data
def form(request):
form_obj = RegForm()
if request.method == "POST":
form_obj = RegForm(request.POST)
if form_obj.is_valid():
print(form_obj.cleaned_data)
del form_obj.cleaned_data["re_pwd"]
models.UserInfo.objects.create(**form_obj.cleaned_data)
return HttpResponse('ojbk')
return render(request, "reg.html", {"form_obj": form_obj}, )
reg.html
// reg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册表单</title>
</head>
<body>
<link rel="stylesheet" href="/static/bootstrap.min.css">
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form action="/form/" method="post" novalidate autocomplete="off">
{% csrf_token %}
<div class="form_group {% if form_obj.user.errors.0 %} has-error{% endif %}">
{{ form_obj.user.label }}
{{ form_obj.user }}
<span class="help-block">{{ form_obj.user.errors.0 }}</span>
</div>
<div class="form_group {% if form_obj.pwd.errors.0 %} has-error {% endif %}">
{{ form_obj.pwd.label }}
{{ form_obj.pwd }}
<span class="help-block">{{ form_obj.pwd.errors.0}}</span>
</div>
<div class="form_group {% if form_obj.re_pwd.errors.0 %} has-error {% endif %}">
{{ form_obj.re_pwd.label }}
{{ form_obj.re_pwd }}
<span class="help-block">{{ form_obj.re_pwd.errors.0}}</span>
</div>
<div class="form_group {% if form_obj.email.errors.0 %} has-error {% endif %}">
{{ form_obj.email.label }}
{{ form_obj.email }}
<span class="help-block">{{ form_obj.email.errors.0}}</span>
</div>
<div class="form_group {% if form_obj.mobile.errors.0 %} has-error {% endif %}">
{{ form_obj.mobile.label }}
{{ form_obj.mobile }}
<span class="help-block">{{ form_obj.mobile.errors.0}}</span>
</div>
<button type="submit">提交啊</button>
</form>
</div>
</div>
</div>
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap.min.js"></script>
</body>
</html>
渲染标签的几种方式
1) {{ form_obj.pwd.label }} {{ form_obj.pwd }}
2) {% for fields in form %}
{{ field.label }}
{{ field }}
{% endfor %}
表单输出选项
-
{{ form.as_table }}:以表格形式加载表单元素 要加<table> {{ form.as_table }}</table>
-
{{ form.as_p }}:以段落形式加载表单元素
-
{{ form.as_ul }:以列表形式加载表单元素 要加<ul> {{ form.as_ul }}</ul>
表单相关的属性
-
{{ field.label }}:字段对应的<lable>标签的文字,例如“发件人”。
-
{{ field.label_tag }}:字段对应的<lable>标签。
-
{{ field.id_for_label }}:字段的“id”属性值。
-
{{ field.value }}:字段的值,例如标题的内容。
-
{{ field.html_name }}:字段对应的HTML标签“name”属性的值。
-
{{ field.help_text }}:字段的帮助文本。
-
{{ field.errors }}:包含任何字段验证错误的全部信息,可以通过“{% for error in field.errors %}”的方式遍历。
-
{{ field.is_hidden }}:字段是否隐藏字段,获取到的是布尔值。
-
{{ field.field }}:字段对象,可以通过它访问字段的属性,例如“{{ field.field.max_length }}”,“{{ field.field.required}}”。
静态字段(只有choice)
实时刷新, 不用重启django
1)重写__init__
from django import forms
from django.forms import widgets, fields
class A(forms.Form):
price = fields.IntegerField()
user_id = fields.IntegerField(
widget=widgets.Select()
# widget=widgets.Select(choices=models.Userinfo.objects.valuse_list('id', 'username'))
# 实际要查上面的, 但是每次最先加载__init__, 加载init时就已经查找完了, 在这里如果再查找就重复了
)
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
self.fields['user_id'].widget.choices = models.Userinfo.objects.valuse_list('id', 'username')
2)自动更新(ModelChoiceField)
在views.py中的form写法
class UserForm(forms.Form):
username = ModelChoiceField(queryset=models.UserInfo.objects.all(), to_field_name='指定在数据库的叫什么')
但是也带来了前端显示不正常的问题
所以要在models中加
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=33)
def __str__(self):
return self.username
form表单字段
from django import forms
from django.forms import widgets, fields
下拉框
单选select(initial默认选2)
user = fields.CharField(initial=2, widget=widgets.Select(choices=((1, 'x'), (2, 'a'))))
# user = fields.ChoiceField(choices=(), widget=widgets.Select())
多选select(attrs 给它加属性)
user = fields.MultipleChoiceField(choices=(), widget=widgets.SelectMultiple(attrs={'class': ''}))
单选框
user = fields.CharField(widget=widgets.RadioSelect(choices=()))
# user = fields.ChoiceField(choices=(), widget=widgets.RadioSelect)
复选框
单选
user = fields.ChoiceField(widget=widgets.CheckboxInput(attrs=()))
多选
user = fields.MultipleChoiceField(choices=(), widget=widgets.CheckboxSelectMultiple)
modelform
class Meta:
model, # 对应Model的
fields=None, # 字段
exclude=None, # 排除字段
labels=None, # 提示信息
help_texts=None, # 帮助提示信息
widgets=None, # 自定义插件
error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
field_classes=None # 自定义字段类 (也可以自定义字段)
localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
model | Forms |
一对多 | forms.ModelChoiceField |
多对多 | forms.ModelMaltipleChiceField |
一对多的type: <class 'django.forms.model.ModelChoiceField'>
多对多的type: <class 'django.forms.model.ModelMultiChoiceField'>
判断forn或modelform渲染的字段是不是一对多或多对多
from django.forms.model import ModelChiceField
form = ModelFormDemo()
for bfield in form:
if istance(bfield.field, ModelChoiceField):
# 是否一对多字段
bfield.is_pop = True
# 对象可以 .xx给它加属性
'''
所以前端可以这样
{% if field.is_pop %}
可以判断是否是一对多, 多对多
'''
print(bfield.field.queryset.model)
# 打印 <class 'app01.model.Publish'>
print(bfield.field.queryset.model._meta.model_name)
# Publish
form组件的limit_chocie_to={}
作用于一对多和多对多
teacher = models.ManyToManyField(..., limit_choice_to = {"depart__in": [1002, 1005]})
相应的也可以在forms中
data = UserInfo.objects.filter(depart__in=[1002, 1005]) .values(''pk", "title")
tracher = forms.ModelMutichoice(choices=data)
实例
views.py
modelform 同样可以用全局的和局部的钩子
from django.forms import ModelForm
class UserForm(ModelForm):
# 重写user字段, 并覆盖
# user = fields.ChoiceField(choices=((1, '男'), (2, '女')))
class Meta:
model = models.UserInfo
fields = "__all__"
# fields = ['xx', 'xx'] 指定那个字段
# exclude = ['xx', 'xx'] 排除那个字段
error_messages = {
'email': {
'invalid': '格式不对'
}
}
labels = {
# 给每个标签指定别名
'user': '用户名啊',
'pwd': '密码呀'
}
widgets = {
# 渲染标签
# 'user': widgets.Textarea(attrs={'class': 'c1'})
}
def clean_user(self):
value = self.cleaned_data.get('user')
a = ["敏感", "词汇"]
for i in a:
if i in value:
raise ValidationError('形式所迫')
is_exist = models.UserInfo.objects.filter(user=value)
if is_exist:
raise ValidationError('用户名重了, 老弟')
return value
def index(request):
if request.method == "POST":
myForm = UserForm(request.POST)
if myForm.is_valid():
myForm.save()
return HttpResponse('ok')
# 没写else 没显示错误信息
else:
return render(request, "test.html", {"form": myForm})
myForm = UserForm()
return render(request, "test.html", {"form": myForm})
models
from django.db import models
class UserInfo(models.Model):
# 每个字段指定verbose_name, 则modelform显示指定内容, 也可以在modelform里指定
id = models.AutoField(primary_key=True, )
user = models.CharField(max_length=32, unique=True, null=False, )
pwd = models.CharField(max_length=10, )
email = models.EmailField(default="aki@gmail.com", verbose_name='邮件')
mobile = models.CharField(max_length=11, verbose_name='手机')
class Meta:
verbose_name = "项目名称"
unique_together = ('user',)
def __str__(self):
return self.user
tses.html
<form action="/index/" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
{{ field.label }}
{{ field }}
<span>{{ field.errors.0 }}</span>
</div>
{% endfor %}
<label for="i1">提交按钮</label>
<input type="submit" value="提交" id="i1">
</form>
下面是modelform编辑的demo
def edit(request, nid):
edi_obj = UserForm.object.filter(pk=nid).first()
if request == "POST":
form = UserForm(request.POST, instance=nid)
# instance 说明修改哪一个, 不传就成了添加
if form.is_valid():
form.save()
return HttpResponse("ok")
form = UserForm(instance=nid)
# get 请求时input 有默认值
return render(request, 'xxx.html', locals())