Django的model form组件

简介:这个组件的功能就是把model和form组合起来,先来一个简单的例子来看一下这个东西怎么用:比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,等等一大堆信息,现在让你写一个创建学生的页面,你应该怎么写呢?首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后台一个一个接收用户的输入,创建一个新的学生对象,保存其实,重点不是这些,而是合法性验证,我们需要在前端判断用户输入是否合法,比如姓名必须在多少字符以内,电话号码必须是多少位的数字,邮箱必须是邮箱的格式,这些当然可以一点一点手动写限制,各种判断,这毫无问题,除了麻烦我们现在有个更优雅的方法:那就是通过ModelForm来实现。

model form的使用

下面将使用model form实现用户注册功能:

models.py

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):  #相当于继承了django的auth_user表
    tel = models.CharField(max_length=32)

注意settings里面要配置:

AUTH_USER_MODEL = 'app01.UserInfo'

form.py

 1 from django import forms
 2 from app01.models import UserInfo
 3 from django.forms import widgets as wid
 4 from django.core.exceptions import ValidationError
 5 
 6 class UserModelForm(forms.ModelForm):
 7 
 8     r_pwd = forms.CharField(label='确认密码',widget=wid.PasswordInput(attrs={'class':'input','id':'r_pwd'}),error_messages={'required': '不能为空'})
 9     class Meta:
10         model = UserInfo   #对应的model中的类
11         #fields = '__all__'  #表示列出所有的字段
12         fields = ['username', 'password','r_pwd', 'email', ]    #选择性列出列表中选中的字段
13         exclude = None     #排除的字段
14 
15        #自定义在前段显示的文本
16         labels = {
17             'password': '密码',
18             'username': '用户名',
19             'email': '邮箱'
20         }
21 
22         #自定义属性
23         widgets = {
24             'username': wid.TextInput(attrs={'class': 'input', 'id': 'username'}),
25             'password': wid.PasswordInput(attrs={'class': 'input', 'id': 'password'}),
26             'email': wid.EmailInput(attrs={'class': 'input', 'id': 'email'})
27         }
28 
29         #自定义错误信息
30         error_messages = {
31             "username":{"required":"不能为空"},
32             "password":{"required":"不能为空"},
33             "email":{"required":"不能为空",'invalid':'邮箱格式错误'}
34         }
35 
36 
37     # 局部钩子,进行第二次过滤
38     def clean_username(self):
39         val = self.cleaned_data.get('username')
40         ret = UserInfo.objects.filter(username=val).first()
41         if not ret:  # 数据库找不到这个用户
42             return val
43         else:
44             raise ValidationError('用户已存在!')
45 
46     def clean_password(self):
47         val = self.cleaned_data.get('password')
48         if val.isdigit():
49             raise ValidationError('密码不能是纯数字!')
50         else:
51             return val
52 
53     def clean_email(self):
54         val = self.cleaned_data.get('email')
55         print('邮箱',val)
56         if not val.endswith('163.com'):
57             raise ValidationError('邮箱必须是163邮箱!')
58         else:
59             return val
60 
61     #定义全局钩子
62     def clean(self):
63         pwd=self.cleaned_data.get('password')
64         r_pwd=self.cleaned_data.get('r_pwd')
65         if pwd and r_pwd and pwd != r_pwd:
66             self.add_error('r_pwd',ValidationError('两次密码不一致!'))   #给全局钩子自定义一个名称,不在是统一的__all__
67         else:
68             return self.cleaned_data

 

字段类型

 

生成的 Form 类将按照 fields 属性中指定的顺序为每个指定的模型字段设置一个表单字段。

 

每个模型字段都有一个对应的默认表单字段。例如,模型中的 CharField 在表单中被表现为 CharField 。 ManyToManyField 则表现为 MultipleChoiceField 。以下是完整的转化清单:

模型字段表单字段
AutoField 不呈现在表单中
BigAutoField 不呈现在表单中
BigIntegerField IntegerField 将 min_value 设置为-9223372036854775808,将 max_value 设置为9223372036854775807。
BooleanField BooleanField
CharField CharField 将 max_length 设置为模型字段的 max_length ,如果模型中设置了 null=True ,会将 empty_value 设置为 None 。
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 和 ManyToManyField 模型字段类型是特殊情况:

  • ForeignKey 由 django.forms.ModelChoiceField 表示, 它是一个 ChoiceField ,其选项是一个模型的 QuerySet
  • ManyToManyField 由 django.forms.ModelMultipleChoiceField 表示,它是一个 MultipleChoiceField ,其选项为一个模型 QuerySet 。

另外,每个生成的表单字段的属性设置如下:

  • 如果模型字段设置了 blank=True ,那么表单字段的 required 属性被设置为 False ,否则 required=True 。
  • 表单字段的 label 设置为模型字段的 verbose_name ,并且首字母大写。
  • 表单字段的 help_text 设置为模型字段的 help_text 。
  • 如果模型字段设置了 choices ,那么表单字段的 widget 会被设置为 Select ,其选项来自模型字段的 choices 。这些选项通常包含一个默认选中的空选项。如果字段设置了必填,则会强制用户进行选择。如果模型字段设置了 blank=False 以及一个明确的 default 值,则表单字段中不会包含空选项(默认会选中 default 值)。

 

views.py   逻辑代码

 1 from django.http import JsonResponse
 2 from app01.form import UserModelForm
 3 
 4 def register(request):
 5 
 6 
 7     if request.method == 'GET':
 8         form = UserModelForm()   #form为UserModelForm的实例化对象
 9         return render(request, 'reg.html',locals())
10 
11 
12     else:
13         form = UserModelForm(request.POST)
14         res = {'user':None,'err_msg':''}
15 
16         if form.is_valid():    #对数据进行校验
17             user = form.cleaned_data.get('username')   #form.cleaned_data为一个字典,里面存储着符合条件的字段
18             res['user'] = user
19             user_obj = form.save()    #相当于create操作
20 
21         else:
22             errors=form.errors  #form.errors为一个字典,键是错误的字段,值是相关的错误的信息
23             res['err_msg'] = errors
24         return JsonResponse(res)

模板:reg.html

 

{% for field in form %}
      <div class="group">
          <label>{{ field.label }}</label>
          {{ field }}<span class="error"></span>
      </div>
{% endfor %}
 <div class="group">
      <input type="submit" class="button" value="注册"id="zhuce">
 </div>


<script src="/static/js/jquery.js"></script>
<script>
    $('#zhuce').click(function () {
        $.ajax({
            url: '/register/',
            type: 'post',
            data: {
                username: $('#username').val(),
                password: $('#password').val(),
                r_pwd: $('#r_pwd').val(),
                email: $('#email').val()
            },
            success: function (response) {
                if (response.user) {
                    location.href = '/books/'
                }
                else {
                    $('.error').html('');
                    $.each(response.err_msg,function (i,j) {
                        $('#'+i).next().html(j[0])
                    });
                 }
            }
        })
    });
</script>                              

 

总结: 从上边可以看到ModelForm用起来是非常方便的,比如增加修改之类的操作。但是也带来额外不好的地方,model和form之间耦合了。如果不耦合的话,mf.save()方法也无法直接提交保存。 但是耦合的话使用场景通常局限用于小程序,写大程序就最好不用了。

 

 

添加记录方法:

form = UserModelForm(request.POST)

 if form.is_valid():    #对数据进行校验

     user_obj = form.save()    #相当于create操作

编辑记录方法:

 

form=BookModelForm(request.POST,instance=edit_book)
if form.is_valid():
      form.save() # update操作 ;  edit_book.update(**cleandata)
      return redirect("/books/")

 

如果不用ModelForm,编辑的时候得显示之前的数据吧,还得挨个取一遍值,如果ModelForm,只需要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就可以得到同样的效果
保存的时候要注意,一定要注意有这个对象(instance=obj),否则不知道更新哪一个数据

 

posted @ 2018-11-06 21:53  中杯可乐不加冰  阅读(174)  评论(0编辑  收藏  举报