Django之ModelForm表单


ModelForm表单

Django中使用Form和ModelForm两个组件实现表单,这里只介绍ModelForm

models.py代码如下:

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=100)
    page = models.IntegerField()
    price = models.FloatField()


class User(models.Model):
    username = models.CharField(max_length=100)
    password = models.CharField(max_length=16)
    telephone = models.CharField(max_length=11)

新建forms.py,代码如下:

from django import forms
from .models import Book


class AddBookForm(forms.ModelForm):
    class Meta:
        model = Book    # 模型表单引用的是哪个模型
        fields = "__all__"  # 引用模型中的全部字段

views.py代码如下:

from django.http import HttpResponse
from .forms import AddBookForm

def add_book(request):
    form = AddBookForm(request.POST)
    if form.is_valid():
        title = form.cleaned_data.get("title")
        page = form.cleaned_data.get("page")
        price = form.cleaned_data.get("price")

        print("title:%s" % title)
        print("page:%s" % page)
        print("price:%s" % price)
        return HttpResponse("success1")
    else:
        print(form.errors.get_json_data())
        return HttpResponse("FAIL")

其中cleaned_data属性实现合法数据,不合法的数据会被cleaned_data自动清洗掉。

在postman中向’http://127.0.0.1:8000/front/add_book/'发送post请求,输入三个参数,能够正确请求成功。少输入参数或参数的值类型不对,也会给出错误提示。

上面的forms中是引用了模型中的全部字段,也可以引用部分字段。

指定引用的字段

from django import forms
from .models import Book


class AddBookForm(forms.ModelForm):
    class Meta:
        model = Book    # 模型表单引用的是哪个模型
        fields = ["title", "page"]  # 指定要引用模型中的字段

排除不需要的字段

from django import forms
from .models import Book


class AddBookForm(forms.ModelForm):
    class Meta:
        model = Book    # 模型表单引用的是哪个模型
        exclude = ["price"]  # 排除不需要引用的字段

在上面的代码中,添加图书,运行if form.is_valid():通过时,不需要进行繁琐的赋值传值,可以直接调用save()方法进行保存。这个方法要在clean没有问题后才能使用(clean后的数据都是符合规则的有效数据)。如果在调用save方法是,传入了一个参数commit=False,那么只会生成这个模型的对象,而不会把这个对象直接插入到数据库中。(执行save()方法有一个前提,就是表单所需的数据都已经涵盖了对应模型的数据,否则有可能会因为字段缺失导致保存失败。)

def add_book(request):
    form = AddBookForm(request.POST)
    if form.is_valid():
        # title = form.cleaned_data.get("title")
        # page = form.cleaned_data.get("page")
        # price = form.cleaned_data.get("price")
        #
        # print("title:%s" % title)
        # print("page:%s" % page)
        # print("price:%s" % price)
        form.save()
        return HttpResponse("success1")
    else:
        print(form.errors.get_json_data())
        return HttpResponse("FAIL")

表单验证数据

常用的Field

使用Field可以是对数据验证的第一步期望提交过来的数据是什么类型,那么就使用什么类型的Field。

CharField

用来接收文本。

参数:

  • max_length:字段值的最大长度;
  • min_length:字段值的最小长度;
  • required:字段是否是必须的。默认是必须的;
  • error_messages:在验证失败的时候,给出错误信息;
EmailField

用来接收邮件,会自动验证邮件是否合法。

错误信息的key:requiredinvalid

FloatField

用来接收浮点类型,并且如果验证通过后,会将这个字段的值转为浮点类型。

  • max_length:字段值的最大长度;
  • min_length:字段值的最小长度;

错误信息的key:requiredinvalidmax_valuemin_value

IntegerField

用来接收整型,并且验证通过后,会将这个字段的值转换为整型。

  • max_length:字段值的最大长度;
  • min_length:字段值的最小长度;

错误信息的key:requiredinvalidmax_valuemin_value

URLField

用来接收url格式的字符串。

错误信息的key:requiredinvalid

forms.py

from django import forms


class MyForm(forms.Form):
    email = forms.EmailField()

views.py

from django.http import HttpResponse
from django.views.generic import View
from django.http import HttpResponse
from .forms import MyForm


def index(request):
    return HttpResponse("success")


class IndexView(View):
    def get(self, request):
        return HttpResponse("get success")

    def post(self, request):
        form = MyForm(request.POST)
        if form.is_valid():
            return HttpResponse("post success")
        else:
            print(form.errors.get_json_data())
            return HttpResponse('post fail')

使用postman创建请求,url:http://127.0.0.1:8000/front1/index/ ,参数是email

email为空时,发送请求

{'email': [{'message': '这个字段是必填项。', 'code': 'required'}]}

email填入不符合规则的字符串,发送请求

{'email': [{'message': '输入一个有效的 Email 地址。', 'code': 'invalid'}]}

可以在forms.py中自定义错误提示的内容

from django import forms


class MyForm(forms.Form):
    email = forms.EmailField(
        error_messages={"required": "邮箱不能为空_plscript", "invalid": "邮箱格式不正确,请输入正确的邮箱_plscript"})

再次发送上面的两个请求

{'email': [{'message': '邮箱不能为空_plscript', 'code': 'required'}]}
{'email': [{'message': '邮箱格式不正确,请输入正确的邮箱_plscript', 'code': 'invalid'}]}

常用的验证器

在验证某个字段的时候,可以传递一个validators参数用来指定验证器,进一步对数据进行过滤。验证器有很多,不过很多验证器其实已经通过Field或者一些参数就可以指定了。比如EmailValidator,可以同EmailField来指定;比如MaxValueValidator可以通过max——value参数来指定。常用的验证器:

  • MaxValueValidator:验证最大值;
  • MinValueValidator:验证最小值;
  • MinLengValidator:验证最小长度;
  • MaxLengValidator:验证最大长度;
  • EmailValidator:验证是否是邮箱格式;
  • URLValidator:验证是否是URL格式;
  • RegexValidator:若还需要更复杂的验证,可以通过正则表达式来验证。

在上面的例子基础上修改forms.py

from django import forms
from django.core import validators


class MyForm(forms.Form):
    # email = forms.EmailField(
    #     error_messages={"required": "邮箱不能为空_plscript", "invalid": "邮箱格式不正确,请输入正确的邮箱_plscript"})
    email = forms.CharField(validators=[validators.EmailValidator(message="邮箱验证失败,请输入有效的邮箱")])
    telephone = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}', message="请输入正确的手机号码!")])

用postman发送请求

{'email': [{'message': '邮箱验证失败,请输入有效的邮箱', 'code': 'invalid'}]}
{'telephone': [{'message': '请输入正确的手机号码!', 'code': 'invalid'}]}

自定义验证

对字段进行自定义方法验证时,新建验证方法以’clean_’开头加上要验证的字段名。当views视图中执行到if form.is_valid():时会自动执行自定义方法clean_字段名

新建register app,

models.py

from django.db import models


class User(models.Model):
    username = models.CharField(max_length=100)
    telephone = models.CharField(max_length=11, null=True, blank=True)

views.py

from django.views.generic import View
from django.http import HttpResponse
from .forms import RegisterForm
from .models import User


class RegisterView(View):
    def get(self, request):
        return HttpResponse("get success")

    def post(self, request):
        form = RegisterForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get("username")
            telephone = form.cleaned_data.get("telephone")
            User.objects.create(username=username, telephone=telephone)
            return HttpResponse("注册成功!")
        else:
            print(form.errors.get_json_data())
            return HttpResponse("注册失败!")

forms.py

from django import forms
from django.core import validators
from .models import User


class RegisterForm(forms.Form):
    username = forms.CharField(max_length=100)
    telephone = forms.CharField(
        validators=[validators.RegexValidator(r'1[3456789]\d{9}', message="请输入正确的手机号码!")])
    pwd = forms.CharField(max_length=16, min_length=6)
    verify_pwd = forms.CharField(max_length=16, min_length=6)

    # 对字段进行自定义方法验证时,新建验证方法以'clean_'开头加上要验证的字段名
    # 当views视图中执行到'if form.is_valid():'时会自动执行自定义方法'clean_字段名'
    # 下面的方法判断提交的手机号码是否已经被注册了
    def clean_telephone(self):
        telephone = self.cleaned_data.get('telephone')
        exists = User.objects.filter(telephone=telephone).exists()
        if exists:
            raise forms.ValidationError(message="%s已经被注册!" % telephone)
        return telephone

    # 重写clean方法,执行到clean()方法,表示之前每个字段都已经验证ok的
    def clean(self):
        cleaned_data = super().clean()
        pwd = cleaned_data.get('pwd')
        verify_pwd = cleaned_data.get('verify_pwd')
        if pwd != verify_pwd:
            raise forms.ValidationError(message="两次密码输入不一致")
        return cleaned_data

执行

{'telephone': [{'message': '这个字段是必填项。', 'code': 'required'}]}
{'telephone': [{'message': '13888888888已经被注册!', 'code': ''}]}
{'__all__': [{'message': '两次密码输入不一致', 'code': ''}]}

提取错误信息

如果验证失败了,那么有一些错误信息是需要传给前端的。可以通过下面的属性来获取:

  • form.errors:这个属性获取的错误信息是一个包含了html标签的错误信息;
  • form.errors.get_json_data():这个方法获取到的是一个字典类型的错误信息。将某个字段的名字作为key,错误信息作为其值;
  • form.as_json():这个方法是将form.get_json_data()返回的字典dump成json格式的字符串。

在forms.py中封装一个精简错误信息的方法

def get_errors(self):
    error_datas = self.errors.get_json_data()
    new_errors = {}
    for key, message_dicts in error_datas.items():
        messages = []
        for message in message_dicts:
            message = message['message']
            messages.append(message)
        new_errors[key] = messages
    return new_errors

在views.py中将输出错误提示的代码替换成上面的方法

class RegisterView(View):
    def get(self, request):
        return HttpResponse("get success")

    def post(self, request):
        form = RegisterForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get("username")
            telephone = form.cleaned_data.get("telephone")
            User.objects.create(username=username, telephone=telephone)
            return HttpResponse("注册成功!")
        else:
            # print(form.errors.get_json_data())
            print(form.get_errors())
            return HttpResponse("注册失败!")
{'telephone': ['请输入正确的手机号码!'], 'pwd': ['确保该变量至少包含 6 字符(目前字符数 4)。'], 'verify_pwd': ['确保该变量至少包含 6 字符(目前字符数 4)。']}

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
  目录