普通版手写注册
def register(request): errors = { 'name':'','pwd':''} if request.method == 'POST': name=request.POST.get('name') pwd=request.POST.get('pwd') if 'sb' in name: errors['name'] = '名字中不能有"sb"' if len(pwd)<6: errors['pwd'] = '密码长度至少6位' return render(request,'reg.html',locals())
注册
#forms组件 先自己实现注册功能,并且对用户输入的信息加限制条件 如果用户输入的信息不符合条件,前端展示报错信息#注册示例: 1.前端渲染标签获取用户输入 >>> 前端渲染标签 2.后端获取用户输入,进行数据校验 >>> 数据校验 3.校验过后产生的提示信息返回给前端 >>> 展示校验信息#数据校验: 前端后端都必须做数据校验,前端做简单的校验,分担后端的压力,后端做的目的,防止拿到url直接访问后端!(防爬虫!!!) #forms组件就能帮我们完成上面的三步: 1.生成前端html代码 2.校验数据 3.展示校验信息
forms组件的使用
#1、实例化RegForm传值 # 注意传入的字典的key必须跟类里面的变量名一致,校验的数据可以多传,但是不能少传 res = views.RegForm({ 'name':'dsb','pwd':'123','email':'123@qq.com'}) #2、数据是否合法 res.is_valid() # 如果数据全部校验通过才为True否则均为False#3、查看校验通过的数据 res.cleaned_data # 会将校验通过的数据都放入cleaned_data中#4、查看校验失败的数据 res.errors # 会将校验失败的数据的提示信息都放入errors中 """ { 'name': ['Ensure this value has at most 6 characters (it has 7).'], 'pwd': ['Ensure this value has at least 3 characters (it has 2).'] } """
1、校验数据
#1、先定义好一个RegForm类 from django import forms class RegForm(forms.Form): # forms组件中定义的字段,默认都是必须传值的 name = forms.CharField(max_length=6,label='用户名') pwd = forms.CharField(max_length=8,min_length=3,label='密码',error_message={ 'max_length':密码最长8位}) email = forms.EmailField()#2、再写一个视图函数 def reg(request): form_obj = RegForm() if request == 'POST': form_obj = RegForm(request.POST) if form_obj.is_valid(): return HttpResponse('注册成功') return render(request,'reg.html')
2、渲染标签
#form组件值渲染获取用户输入(或选择的...只要是用户操作都算)的标签提交按钮需要我们自己手动写#三种方式:(form_obj是实例化RegForm的类) 1.{ { form_obj.as_p }} #as_p渲染出来被p标签包裹 2.{ { form_obj.name.label }}{ { form_obj.name }} #这里form_obj.name其实是一个input框,form_obj.name.lable是input框前边的名称,后端传入的参数,如name,pwd等等 #lable在forms组件字段定义阶段设置 3.{% for foo in form_obj %}{
{ foo.label }}{ { foo }} #这里的foo.label相当于上边的form_obj.name {% endfor %}#展示报错信息
局部钩子与全局钩子
from django.core.exceptions import ValidationError# 局部钩子(对某个字段添加限制时)def clean_name(self):name = self.cleaned_data.get('name') if '666' in name: # 局部钩子中可以手动添加报错信息 self.add_error('name','光喊666是不行的,得有操作!') # 也可以主动抛出异常 # raise ValidationError('光喊666是不行的,得有操作!') # 拿出来校验的数据必须返回回去 return name# 全局钩子 def clean(self): pwd = self.cleaned_data.get('pwd') conf_pwd = self.cleaned_data.get('confirm_pwd') if pwd == confirm_pwd: # 校验过后一定要把cleaned_data再返回出来 return self.cleaned_data else: #需要手动将报错信息加入到errors里面 self.add_error('conf_pwd', '两次密码不一致')"""局部和全局添加报错信息,都可以通过self.add_error('字段','提示信息')局部钩子中还可以通过抛出异常的方式提示校验信息"""
from app01 import models def reg3(request): # 实例化一个form对象 form_obj = RegForm() if request.method == 'POST': print(request.POST) # 直接丢给form组件需要注意的前端的input name属性必须跟form里面字段的名一致 form_obj = RegForm(request.POST) if form_obj.is_valid(): models.User.objects.create(**form_obj.cleaned_data) return HttpResponse('ok') return render(request, 'reg2.html', locals())总结:forms组件可以实现三大功能,其实它的这些功能都是给我的models.py里面的一张张模型表服务的
常用字段与插件
创建Form类时,主要涉及到字段和插件,字段用于请求数据的验证,插件用于自动生成HTML
initial (input框里的初始值)
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="密码")
PasswordInput (为密码设置密文)(forms.PasswordInput)
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.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() )
多选Select
class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
单选/多选checkbox
class LoginForm(forms.Form): ... keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )多选class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
choice字段注意事项
#在使用选择标签时,需要注意choices的选项可以配置从数据库中获取,但是由于是静态字段 获取的值无法实时更新,需要重写构造方法从而实现choice实时更新。方式一from django.forms import Formfrom django.forms import widgetsfrom 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 formsfrom django.forms import fieldsfrom 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-01TimeField(BaseTemporalField) 格式:11:12DateTimeField(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类型
字段校验
RegexValidator验证器
from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
自定义验证函数
import refrom django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom 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'邮箱'}))
补充进阶
应用Bootstrap样式
login
批量添加样式(可通过重写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' })
ModelForm
通常在Django项目中,我们编写的大部分都是与Django 的模型紧密映射的表单。 举个例子,你也许会有个Book 模型,并且你还想创建一个form表单用来添加和编辑书籍信息到这个模型中。 在这种情况下,在form表单中定义字段将是冗余的,因为我们已经在模型中定义了那些字段。
基于这个原因,Django 提供一个辅助类来让我们可以从Django 的模型创建Form,这就是ModelForm。
modelForm定义
form与model的终极结合。
class BookForm(forms.ModelForm): class Meta: model = models.Book fields = "__all__" labels = { "title": "书名", "price": "价格" } widgets = { "password": forms.widgets.PasswordInput(attrs={"class": "c1"}), }
class Meta下常用参数:
model = models.Book # 对应的Model中的类fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段exclude = None # 排除的字段labels = None # 提示信息help_texts = None # 帮助提示信息widgets = None # 自定义插件error_messages = None # 自定义错误信息
ModelForm的验证
与普通的Form表单验证类型类似,ModelForm表单的验证在调用is_valid() 或访问errors 属性时隐式调用。
我们可以像使用Form类一样自定义局部钩子方法和全局钩子方法来实现自定义的校验规则。
如果我们不重写具体字段并设置validators属性的化,ModelForm是按照模型中字段的validators来校验的。
save()方法
每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例:
>>> from myapp.models import Book>>> from myapp.forms import BookForm# 根据POST数据创建一个新的form对象>>> form_obj = BookForm(request.POST)# 创建书籍对象>>> new_ book = form_obj.save()# 基于一个书籍对象创建form对象>>> edit_obj = Book.objects.get(id=1)# 使用POST提交的数据更新书籍对象>>> form_obj = BookForm(request.POST, instance=edit_obj)>>> form_obj.save()