模型表单ModelForm
一、基本用法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
from django import formsfrom django.utils.safestring import mark_safefrom django.core.exceptions import ValidationErrorfrom rbac import modelsfrom django.utils.translation import ugettext_lazyICON_LIST = [ [‘fa-hand-scissors-o‘, ‘<i aria-hidden="true" class="fa fa-hand-scissors-o"></i>‘], [‘fa-hand-spock-o‘, ‘<i aria-hidden="true" class="fa fa-hand-spock-o"></i>‘],]for item in ICON_LIST: item[1] = mark_safe(item[1])class BootStrapModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(BootStrapModelForm, self).__init__(*args, **kwargs) # 统一给ModelForm生成字段添加样式 for name, field in self.fields.items(): field.widget.attrs[‘class‘] = ‘form-control‘"""基本用法:首先从django.forms导入ModelForm;编写一个自己的类,继承ModelForm;在新类里,设置元类Meta;在Meta中,设置model属性为你要关联的ORM模型,这里是Menu;在Meta中,设置fields属性为你要在表单中使用的字段列表;列表里的值,应该是ORM模型model中的字段名。"""class UserModelForm(BootStrapModelForm): confirm_password = forms.CharField(label=‘确认密码‘) # class Meta: model = models.UserInfo fields = [‘name‘, ‘email‘, ‘password‘, ‘confirm_password‘, ‘icon‘] # fields = ‘__all__‘ #表示将映射的模型中的全部字段都添加到表单类中来 exclude = [‘pid‘] #表示将model中,除了exclude属性中列出的字段之外的所有字段,添加到表单类中作为表单字段。 widgets = { ‘name‘: forms.TextInput(attrs={‘class‘: ‘form-control‘}), ‘icon‘: forms.RadioSelect( choices=ICON_LIST, attrs={‘class‘: ‘clearfix‘} ) } labels = { ‘name‘: ugettext_lazy(‘Writer‘), } help_texts = { ‘name‘: ugettext_lazy(‘Some useful help text.‘), } error_messages = { ‘name‘: { ‘max_length‘: ugettext_lazy("This writer‘s name is too long."), }, } def clean_confirm_password(self): """ 检测密码是否一致 :return: """ password = self.cleaned_data[‘password‘] confirm_password = self.cleaned_data[‘confirm_password‘] if password != confirm_password: raise ValidationError(‘两次密码输入不一致‘) return confirm_password# 可以在实例化一个表单时通过指定initial参数来提供表单中数据的初始值。 |
二、字段类型
生成的Form类中将具有和指定的模型字段对应的表单字段,顺序为fields属性列表中指定的顺序。
每个模型字段有一个对应的默认表单字段。比如,模型中的CharField表现成表单中的CharField。模型中的ManyToManyField字段会表现成MultipleChoiceField字段。下面是完整的映射列表:
| 模型字段 | 表单字段 |
|---|---|
| AutoField | 在Form类中无法使用 |
| BigAutoField | 在Form类中无法使用 |
| BigIntegerField | IntegerField,最小-9223372036854775808,最大9223372036854775807. |
| BooleanField | BooleanField |
| CharField | CharField,同样的最大长度限制。如果model设置了null=True,Form将使用empty_value |
| CommaSeparatedIntegerField | CharField |
| DateField | DateField |
| DateTimeField | DateTimeField |
| DecimalField | DecimalField |
| EmailField | EmailField |
| FileField | FileField |
| FilePathField | FilePathField |
| FloatField | FloatField |
| ForeignKey | ModelChoiceField |
| ImageField | ImageField |
| IntegerField | IntegerField |
| IPAddressField | IPAddressField |
| GenericIPAddressField | GenericIPAddressField |
| ManyToManyField | ModelMultipleChoiceField |
| NullBooleanField | NullBooleanField |
| PositiveIntegerField | IntegerField |
| PositiveSmallIntegerField | IntegerField |
| SlugField | SlugField |
| SmallIntegerField | IntegerField |
| TextField | CharField,并带有widget=forms.Textarea参数 |
| TimeField | TimeField |
| URLField | URLField |
-
ForeignKey被映射成为表单类的django.forms.ModelChoiceField,它的选项是一个模型的QuerySet,也就是可以选择的对象的列表,但是只能选择一个。
-
ManyToManyField被映射成为表单类的django.forms.ModelMultipleChoiceField,它的选项也是一个模型的QuerySet,也就是可以选择的对象的列表,但是可以同时选择多个,多对多嘛。
- 如果模型字段设置blank=True,那么表单字段的required设置为False。 否则,required=True。
- 表单字段的label属性根据模型字段的verbose_name属性设置,并将第一个字母大写。
- 如果模型的某个字段设置了editable=False属性,那么它表单类中将不会出现该字段。道理很简单,都不能编辑了,还放在表单里提交什么?
- 表单字段的
help_text设置为模型字段的help_text。 - 如果模型字段设置了choices参数,那么表单字段的widget属性将设置成Select框,其选项来自模型字段的choices。选单中通常会包含一个空选项,并且作为默认选择。如果该字段是必选的,它会强制用户选择一个选项。 如果模型字段具有default参数,则不会添加空选项到选单中。
三、完整示例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#models.pyfrom django.db import modelsTITLE_CHOICES = ( (‘MR‘, ‘Mr.‘), (‘MRS‘, ‘Mrs.‘), (‘MS‘, ‘Ms.‘),)class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): # __unicode__ on Python 2 return self.nameclass Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author)#myforms.pyfrom django import formsclass AuthorForm(forms.ModelForm): class Meta: model = models.Author fields = [‘name‘, ‘title‘, ‘birth_date‘]class BookForm(forms.ModelForm): class Meta: model = models.Book fields = [‘name‘, ‘authors‘]#上面的ModelForm子类基本等同于下面的定义方式(唯一的区别是save()方法):from django import formsclass AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( max_length=3, widget=forms.Select(choices=TITLE_CHOICES), ) birth_date = forms.DateField(required=False)class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all()) |
四、ModelForm的验证
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def menu_add(request): if request.method == ‘POST‘: form = MenuModelForm(data=request.POST) if form.is_valid(): form.save() return redirect(memory_reverse(request, ‘rbac:menu_list‘)) form = MenuModelForm() return render(request, ‘rbac/change.html‘, {‘form‘: form})def menu_edit(request, pk): obj = models.Menu.objects.filter(id=pk).first() if not obj: return HttpResponse(‘菜单不存在‘) if request.method == ‘POST‘: form = MenuModelForm(instance=obj, data=request.POST) if form.is_valid(): form.save() return redirect(memory_reverse(request, ‘rbac:menu_list‘)) form = MenuModelForm(instance=obj) return render(request, ‘rbac/change.html‘, {‘form‘: form}) |