django Modelform 使用
前言:
为什么要用form去验证呢?
我们提交的是form表单,在看前端源码时如果检查到POST URL及我们提交的字段,如果没有验证我们是否可以直接POST数据到URL,后台并没有进行校验,直接处理,那样会不会对我们系统产生影响?答案是肯定的,FORM的作用就是起到一定的数据保护作用加一层校验将不合法数据丢弃
1.针对上一章 django form表单验证 补充
form表单补充,select框
#!/usr/bin/env python # -*- coding: utf-8 -*- import re from django import forms from django.core.exceptions import ValidationError def phone_validate(value): phone_re = re.compile(r'^(13[0-9]|15[012356789]|17[0678]|18[0-9]|14[57])[0-9]{8}$') if not phone_re.match(value): raise ValidationError('手机号码格式错误') class TestForm(forms.Form): user_type_choice = ( (0, u'普通用户'), (1, u'管理员'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"}))
注:
widget 生成前端展示 .SELECT选择下拉框,attrs属性class前端显示类
二. 通过model form来自定义表单
1. 字段类型
生成的表单类中将具有和指定的模型字段对应的表单字段,顺序为fields 属性中指定的顺序。
Form field | |
---|---|
AutoField | Not represented in the form |
BigIntegerField | IntegerField with min_value set to -9223372036854775808 and max_value set to 9223372036854775807. |
BooleanField | BooleanField |
CharField | CharField with max_length set to the model field’s max_length |
CommaSeparatedIntegerField | CharField |
DateField | DateField |
DateTimeField | DateTimeField |
DecimalField | DecimalField |
EmailField | EmailField |
FileField | FileField |
FilePathField | FilePathField |
FloatField | FloatField |
ForeignKey | ModelChoiceField (see below) |
ImageField | ImageField |
IntegerField | IntegerField |
IPAddressField | IPAddressField |
GenericIPAddressField | GenericIPAddressField |
ManyToManyField | ModelMultipleChoiceField (see below) |
NullBooleanField | NullBooleanField |
PositiveIntegerField | IntegerField |
PositiveSmallIntegerField | IntegerField |
SlugField | SlugField |
SmallIntegerField | IntegerField |
TextField | CharField with widget=forms.Textarea |
TimeField | TimeField |
URLField | URLField |
完整示例:
1. 数据模型
创建:
1 from django.db import models 2 3 4 # Create your models here. 5 6 7 class Publisher(models.Model): 8 name = models.CharField(max_length=30, unique=True) 9 address = models.CharField(max_length=50) 10 city = models.CharField(max_length=60) 11 state_province = models.CharField(max_length=60) 12 country = models.CharField(max_length=60) 13 website = models.URLField() 14 15 def __str__(self): 16 return self.name 17 18 19 class Author(models.Model): 20 first_name = models.CharField(max_length=32) 21 last_name = models.CharField(max_length=32) 22 email = models.EmailField() 23 24 def __str__(self): 25 name = self.first_name + self.last_name 26 return name 27 28 29 class Book(models.Model): 30 name = models.CharField(max_length=128) 31 authors = models.ManyToManyField(Author) 32 publisher = models.ForeignKey(Publisher) 33 publish_date = models.DateField() 34 35 def __str__(self): 36 return self.authors
1 DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.mysql', 4 'NAME': os.environ.get("MYSQL_NAME", 'modelform'), 5 'USER': os.environ.get("MYSQL_USER", 'root'), 6 'PASSWORD': os.environ.get("MYSQL_PASSWD", 'password'), 7 'HOST': os.environ.get("MYSQL_HOST", '192.168.1.102'), 8 'PORT': os.environ.get("MYSQL_PORT", 3306), 9 } 10 }
生成:
python manage.py makemigrations
python manage.py migrate
只是举例详见参考
https://docs.djangoproject.com/en/1.11/topics/db/models/
http://www.cnblogs.com/jl-bai/p/5798860.html
注:
报错:
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'
原因:无mysql客户端
解决方案:
在project 的root_dir 下__init__文件中写入
import pymysql pymysql.install_as_MySQLdb()
(一)数据前端展示
2. 后台VIEWS
from django.views.generic.base import TemplateView from app01.forms import BookModelForm class Index(TemplateView): template_name = "index.html" def get(self, request, *args, **kwargs): form = BookModelForm() return self.render_to_response(context={"form": form})
3. ModelForm
#!/usr/bin/env python # -*- coding: utf-8 -*- from django import forms from app01.models import Book class BookModelForm(forms.ModelForm): class Meta: model = Book fields = ['name', 'authors', 'publish_date'] widgets = { 'name': forms.TextInput(attrs={'class': "form-control"}), 'authors': forms.Select(attrs={'class': "form-control"}), 'publish_date': forms.DateInput(attrs={'class': "form-control", 'placeholder': "YYYY-MM-DD"}) }
4. 前端展示
{% load staticfiles %} {% load i18n admin_static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>django model form</title> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"/> </head> <body> <div style="width: 980px;margin: 0 auto"> <div class="col-lg-6"> <form action="{% url 'index' %}" method="POST">{% csrf_token %} {{ form }} <input class="btn btn-primary m-r-5 m-b-5" type="submit" value="提 交"> </form> </div> </div> </body> </html>
访问:
(二) 模型表单的验证
1. 验证模型
与普通的表单验证类型类似,模型表单的验证在调用is_valid() 或访问errors 属性时隐式调用,或者通过full_clean() 显式调用,尽管在实际应用中你将很少使用后一种方法。
模型的验证(Model.full_clean())在表单验证这一步的内部触发,紧跟在表单的clean() 方法调用之后。
警告
Clean 过程会以各种方式修改传递给模型表单构造函数的模型实例。例如,模型的日期字段将转换成日期对象。验证失败可能导致模型实例处于不一致的状态,所以不建议重新使用它。
1.1 重写clean() 方法
可以重写模型表单的clean() 来提供额外的验证,方法和普通的表单一样。
模型表单实例包含一个instance 属性,表示与它绑定的模型实例。
警告
ModelForm.clean() 方法设置一个标识符, 使得模型验证 这一步验证标记为unique、 unique_together 或unique_for_date|month|year 的模型字段的唯一性。
如果你需要覆盖clean() 方法并维持这个验证行为,你必须调用父类的clean() 方法。
与django form表单验证 中定义clean方法相同
模型error_messages 的注意事项
表单字段级别或表单级别的错误信息永远比模型字段级别的错误信息优先。
模型字段的错误信息只用于模型验证步骤引发ValidationError 的时候,且不会有对应的表单级别的错误信息。
2. save()方法
每个ModelForm还具有一个save() 方法。这个方法根据表单绑定的数据创建并保存数据库对象。模型表单的子类可以用关键字参数instance 接收一个已经存在的模型实例;如果提供,save() 将更新这个实例。如果没有提供,save() 将创建模型的一个新实例:
1 class Index(TemplateView): 2 template_name = "index.html" 3 4 def get(self, request, *args, **kwargs): 5 form = BookModelForm() 6 return self.render_to_response(context={"form": form}) 7 8 def post(self,request): 9 form = BookModelForm() 10 if form.is_valid(): 11 form.save() 12 return redirect("home") 13 return self.render_to_response(context={"form": form})
如果表单没有验证,save() 调用将通过检查form.errors 来进行验证。如果表单中的数据不合法,将引发ValueError —— 例如,如果form.errors 为True。
3. 先择用到的字段
class BookModelForm(forms.ModelForm): class Meta: model = Book fields = ['name', 'authors', 'publish_date'] widgets = { 'name': forms.TextInput(attrs={'class': "form-control"}), 'authors': forms.Select(attrs={'class': "form-control"}), 'publish_date': forms.DateInput(attrs={'class': "form-control", 'placeholder': "YYYY-MM-DD"}) }
在ModelForm中我们通常使用fields 属性显式设置所有将要在表单中编辑的字段,如果不这样做,当表单不小心允许用户设置某些特定的字段,特别是有的字段添加到模型中的时候,将很容易导致安全问题。
另外一种方式是自动包含所有的字段,或者排除某些字段。这种基本方式的安全性要差很多,而且已经导致大型的网站受到严重的利用。
3.1 设置fields 属性为特殊的值'__all__' 以表示需要使用模型的所有字段。例如:
class BookModelForm(forms.ModelForm): class Meta: model = Book fields = '__all__'
3.2 设置ModelForm 内联的Meta 类的exclude 属性为一个要从表单中排除的字段的列表
class BookModelForm(forms.ModelForm): class Meta: model = Book exclude = ()
exclude = () 与 fields = '__all__' 达到效果相同选取所有字段
如果 exclude=['name'],那么排除“name"字段其他字段都会显示
3.3 多个外键对同一个模型
如果一个模型在同一个模型中包含多个外键,则需要使用fk_name手动解决歧义。例如,考虑以下模型:
class Friendship(models.Model): from_friend = models.ForeignKey(Friend, related_name='from_friends') to_friend = models.ForeignKey(Friend, related_name='friends') length_in_months = models.IntegerField()
要解决此问题,您可以使用fk_name到inlineformset_factory()
FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name='from_friend', ... fields=('to_friend', 'length_in_months'))
具体参考:
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/