Django中用表单验证前端的数据并进行存储到数据库中

我们要想使django中的表单验证前端传入的数据是否正确,我们需要用到Django中的forms。

常用的Field:

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

1. CharField:

用来接收文本。
参数:

  1. max_length:这个字段值的最大长度。
  2. min_length:这个字段值的最小长度。
  3. required:如果没有写这个参数,即默认required=True,即这个字段不能为空。
  4. error_messages:在某个条件验证失败的时候,给出错误信息。
2. EmailField:

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

3. FloatField:

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

  1. max_value:最大的值。
  2. min_value:最小的值。
    错误信息的key:required、invalid、max_value、min_value。
4. IntegerField:

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

  1. max_value:最大的值。
  2. min_value:最小的值。
5. URLField:

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

字段中的error_messages中的参数字段:
  1. required:传入的字段不能为空。
  2. invalid:字段不合法。
    拥有最大长度和最小长度的才有下面两个错误信息
  3. min_length:少于设置的最短长度。
  4. max_length:大于设置的最大长度。
    拥有最大值和最小值的才有下面两个错误信息
  5. max_value:超过最大值。
  6. min_value:没有达到最小值。

新建一个app,然后在app下面新建一个forms.py的文件,在里面定义一个类,然后继承至djano.froms.Form,然后在里面定义字段。

例如,我们定义一个用户名,一个邮箱,一个价格,一个网址

forms.py中写入:

class MyForm(forms.Form):
    name = forms.CharField(max_length=100,min_length=3,error_messages={'required':'用户名不能为空','min_length':'最少不能少于3个字符','max_length':'最多不能超过100个字符'})
    email = forms.EmailField(error_messages={'invalid':'请输入正确的邮箱!','required':'邮箱不能为空'})
    price = forms.FloatField(error_messages={'invalid':'请输入正确的数字','required':'价格不能为空'})
    url = forms.URLField(error_messages={'invalid':'请输入正确的url~~','required':'url不能为空'})

error_messages是可选参数,如果没有设置这个参数,当前端传入的数据不合法时,显示的错误信息默认时英文的,我们设置了error_messages参数,我们就能呢个自定义我们想要显示的错误信息了。

views中定义一个类视图

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

class FormView(View):
    def get(self,request):
        return render(request,'form.html')

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

如果使用get请求访问这个类视图,就返回一个html页面,如果使用post请求:

对数据进行判断:
先使用我们定义的MyForm类接收前端传入的数据,然后赋值给form,再使用form的is_valid方法对数据进行判断是否合法。如果传入合法,就返回一个success,否则返回fail,并且在控制台打印错误信息。

因为我们上面渲染了一个模板,所以我们需要先去定义一个form.html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <label>用户名:</label>
    <input type="text" name="name"><br>
    <label>邮箱:</label>
    <input type="text" name="email"><br>
    <label>价格:</label>
    <input type="text" name="price"><br>
    <label>网址:</label>
    <input type="text" name="url"><br>
    <input type="submit" value="提交"><br>
</form>

</body>
</html>

然后添加映射,输入数据就能查看对应的效果了,可以自行尝试输入不符合格式的数据。

这样我们就能对一些常用字段进行判断输入的数据是否合法了。

常用验证器:

在验证某个字段的时候,可以传递一个validators参数用来指定验证器,进一步对数据进行过滤。验证器有很多,但是很多验证器我们其实已经通过这个Field或者一些参数就可以指定了。比如EmailValidator,我们定义的EmailFiled这个字段在底层就会使用这个验证器来对数据进行验证,比如MaxValueValidator,定义了max_length之后在底层也会使用这个验证器进行验证。以下是一些常用的验证器:

  1. MaxValueValidator:验证最大值。
  2. MinValueValidator:验证最小值。
  3. MinLengthValidator:验证最小长度。
  4. MaxLengthValidator:验证最大长度。
  5. EmailValidator:验证是否是邮箱格式。
  6. URLValidator:验证是否是URL格式。
  7. RegexValidator:如果还需要更加复杂的验证,那么我们可以通过正则表达式的验证器:RegexValidator。

例如,我们来定义一个CharField字段,再来使用EmailValidator验证器实现对邮箱格式的验证。

from django.core import validators

    test_email = forms.CharField(validators=[validators.EmailValidator(message='请输入正确的邮箱格式')])

将上面的代码添加至MyForm中,然后在前端新增加一个input

    <label>test_email:</label>
    <input type="text" name="test_email"><br>

就能够进行测试了,这里只是做一个验证器的演示,对email进行验证的时候还是用EmailFiled字段比较方便。

重点是在RegexValidator验证器上面,接下在我们使用正则表达式验证器对手机好嘛进行验证:
MyForm中添加字段

    # 以1开头,接下来是3,4,5,6,7,8,然后再是9位数字
       telephone = forms.CharField(validators=[validators.RegexValidator(r'1[345678]\d{9}',message='请输入正确的手机号吗')])

form.html中添加标签:

    <label>电话号码:</label>
    <input type="text" name="telephone"><br>

这样,就完成了对手机号码的验证了。

自定义验证:

有时候对一个字段验证,不是一个长度,一个正则表达式能够写清楚的,还需要一些其他复杂的逻辑,那么我们可以对某个字段,进行自定义的验证。比如在注册的表单验证中,我们想要验证手机号码是否已经被注册过了,那么这时候就需要在数据库中进行判断才知道。对某个字段进行自定义的验证方式是,定义一个方法,这个方法的名字定义规则是:clean_fieldname。如果验证失败,那么就抛出一个验证错误。

例如:
在写注册页面的时候,要验证用户表中手机号码之前是否在数据库中存在。

首先在当前app下创建一个models

from django.db import models

# Create your models here.
class User(models.Model):
    name = models.CharField(max_length=100)
    # 在数据库中,电话号码不能重复
    telephone = models.CharField(max_length=11,unique=True)

然后将此app添加值settings中,配置settings,连接至数据库,然后执行makemigrations,migrate。
就创建好了一个user表。

新建一个register.html的文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>

<form action="" method="post">
    <table>
        <tbody>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>电话号码:</td>
                <td><input type="text" name="telephone"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </tbody>
    </table>
</form>

</body>
</html>

然后在forms.py中定义一个RegisterForm类

from . import models

class RegisterForm(forms.Form):
    username = forms.CharField(max_length=100,min_length=6,error_messages={'required':'用户名不能为空','min_length':'最少不能少于6个字符','max_length':'最多不能超过100个字符'})
    telephone = forms.CharField(validators=[validators.RegexValidator(r'1[345678]\d{9}',message='请输入正确的手机号吗')])

    # 在进行对telephone验证的时候就会自动调用这个方法
    # 这个函数名字不能随便取,只能是  `clean_需要附件验证的字段名字`。
    def clean_telephone(self):
        telephone = self.cleaned_data.get('telephone')
        exists = models.User.objects.filter(telephone=telephone).exists()
        if exists:
            # 验证失败抛出的异常
            raise forms.ValidationError(message='此手机号码已经被注册')
        # 如果验证没有问题,一定要记得将电话好嘛返回回去
        return telephone

然后在views中定义一个类视图:

from . import models
from .forms import MessageBoardForm,MyForm,RegisterForm

class RegisterView(View):
    def get(self,request):
        return render(request,'register.html')
    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 = models.User.objects.create(name=username,telephone=telephone)
            return HttpResponse('注册成功')
        else:
            print(form.errors.get_json_data())
            return HttpResponse('注册失败')

然后添加映射

    path('register/',views.RegisterView.as_view()),

这样,我们就对telephone这个字段进行了深度验证了,即判断当前注册的电话号码是否已经被注册了。

这里只是使用clean_telephone实现了对一个字段进行深入判断,那么如果我们向对多个数据进行判断呢?
这个时候我们就需要重写父类的clean方法了

需求:在注册的时候需要输入两次密码,如果前后两次密码输入不成功,那么也不能成功的进行注册

首先完善前端页面,register.html中在提交按的<tr>标签上面添加两个<tr>标签:

<tr>
    <td>密码:</td>
    <td><input type="password" name="pwd1"></td>
</tr>
<tr>
    <td>确认密码:</td>
    <td><input type="password" name="pwd2"></td>
</tr>

然后完善forms.py中的注册表单RegisterForm

class RegisterForm(forms.Form):
    username = forms.CharField(max_length=100,min_length=6,error_messages={'required':'用户名不能为空','min_length':'最少不能少于6个字符','max_length':'最多不能超过100个字符'})
    telephone = forms.CharField(validators=[validators.RegexValidator(r'1[345678]\d{9}',message='请输入正确的手机号吗')])
    pwd1 = forms.CharField(max_length=16,min_length=6,error_messages={'required':'用户名不能为空','min_length':'最少不能少于6个字符','max_length':'最多不能超过16个字符'})  # 第一次输入的密码
    pwd2 = forms.CharField(max_length=16,min_length=6,error_messages={'required':'用户名不能为空','min_length':'最少不能少于6个字符','max_length':'最多不能超过16个字符'})  # 第二次输入的密码

    # 在进行对telephone验证的时候就会自动调用这个方法
    def clean_telephone(self):
        telephone = self.cleaned_data.get('telephone')
        exists = models.User.objects.filter(telephone=telephone).exists()
        if exists:
            # 验证失败抛出的异常
            raise forms.ValidationError(message='此手机号码已经被注册')
        # 如果验证没有问题,一定要记得将电话好嘛返回回去
        return telephone

    # 对多个字段进行验证,重写clean方法
    # 能来到这个方法,说明前面的所有字段都验证成功了的
    def clean(self):
        cleaned_data = super().clean()
        pwd1 = cleaned_data.get('pwd1')
        pwd2 = cleaned_data.get('pwd2')
        if pwd1 != pwd2:
            raise forms.ValidationError(message='两次输入的密码不一致')
        return cleaned_data

在上面我们就重写了父类的clean方法,然后进行判断,如果没有问题,再将所有数据返回回去。

然后我们修改我们的models.py,将pwd这个字段添加进去。

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    telephone = models.CharField(max_length=11)
    pwd = models.CharField(max_length=20,null=True)

然后执行makemigrations,migrate。将pwd字段添加至表中。

最后,修改views中的RegisterView视图,将验证过的数据存入数据库中。
只需要修改RegisterView视图中的post方法中的if判断条件中的代码

    if form.is_valid():
        username = form.cleaned_data.get('username')
        telephone = form.cleaned_data.get('telephone')
        pwd = form.cleaned_data.get('pwd1')
        user = models.User.objects.create(name=username,telephone=telephone,pwd=pwd)
        return HttpResponse('注册成功')

这样,就实现了对两次密码输入的一致性进行了判断。

上面的代码中,我们只是将错误信息在后台打印了出来,但是我们需要将我们自定义的错误信息展示到前端去,让用户知道哪里错了,所以首先我们的先拿到错误信息。

我们可以在form.py中的RegisterForm中定义一个方法,然后我们需要数据的时候就直接调用这个方法就是了。

    def get_errors(self):
        # 通过self.errors.get_json_data()获取错误的信息
        errors = self.errors.get_json_data()
        # 新建一个错误信息的字典,存储错误信息的字段和对应的错误信息
        new_errors = {}
        # 遍历获取的错误信息字典
        for key,message_dicts in errors.items():
            # 定义一个错误信息的列表,因为一个字段可能出现了多个错误,
            # 所以我们用一个列表将错误某个字段的所有错误信息存储起来
            messages = []
            # 遍历所有的错误信息,然后逐个添加至定义的messages列表中
            for message in message_dicts:
                message = message['message']
                messages.append(message)
            # 将获取的所有错误信息添加至自定义的字典中,并使用获取的key来对应
            new_errors[key] = messages
        # 返回自定义错误信息的字典
        return new_errors

这样,我们就定义好了一个获取错误信息的函数,可以对照者前面我们打印出来的错误信息来理解这段代码。

然后,在views中的post方法中的else中修改代码

        else:
            print(form.get_errors())
            # print(form.errors.get_json_data())
            return HttpResponse('注册失败')

然后我们就能在控制台看到我们自定义的错误信息了。

然后我们只需要用一个变量接收错误信息,然后就能使用render返回给前端了

        else:
            errors = form.get_errors()
            print(errors)
            return render(request,'register.html',{'errors':errors})

这样,我们就实现了一个较为完整的注册页面的后台了。

使用modelForm简化操作

大家在写表单的时候,会发现表单中的Field和模型中的Field基本上是一模一样的,而且表单中需要验证的数据,也就是我们模型中需要保存的。那么这时候我们就可以将模型中的字段和表单中的字段进行绑定。

在models中定义一个Book模型:

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

然后makemigrations,后在migrate。
由上面的知识可知,要使用表单验证数据,我们需要在forms中新建一个表单类,所以我们在forms.py中新建一个类:

from . import models
from django import forms

class AddBookForm(forms.ModelForm):
	class Meta:
		# 指定映射的模型
		model = models.Book
		# 指定模型中需要验证的字段,__all__表示全部
		fields = '__all__'
		# 指定只验证name和page字段
		# fields = ['name','page']
		# 指定不验证price字段
        # exclude = ['price']

这样,我们就定义好一个表单验证器。
注意: fields属性和exclude属性有且只能有一个存在。
然后我们就需要在views中编写视图函数来接收数据了,在views中新建一个类视图:

from .forms import AddBookForm
from django.views.generic import View

class add_book(View):
    def get(self,request):
        return render(request,'add_book.html')
    def post(self,request):
        form = AddBookForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data.get('name')
            page = form.cleaned_data.get('page')
            price = form.cleaned_data.get('price')
            print(name,page,price)
            return HttpResponse('success')
        else:
            print(form.errors.get_json_data())
            return HttpResponse('Fail')

然后新建一个html文件add_book.html,在body中写入一下代码:

<form action="" method="post">
    <label>书名:</label>
    <input type="text" name="name"><br>
    <label>页数:</label>
    <input type="text" name="page"><br>
    <label>价格:</label>
    <input type="text" name="price"><br>
    <input type="submit" value="提交">
</form>

然后在urls中添加映射:

    path('add_book/',views.add_book.as_view()),

然后输入网址即可进行测试了。

当我们输入错误的信息时,在后台打印出的信息时英文的,所以这个时候我们可以和上面一样,自己设置错误信息。

在我们刚才定义的表单验证器中修改代码:

class AddBookForm(forms.ModelForm):
	# 对页数进行深入判断,这里只是演示一下clean_<name>方法,我们要实现这个需求可以直接使用验证器
    def clean_page(self):
        page = self.cleaned_data.get('page')
        if page > 100:
            raise forms.ValidationError('页数不能大于100页')
        return page

    class Meta:
        # 指定映射的模型
        model = models.Book
        # 指定模型中需要验证的字段,__all__表示全部
        fields = '__all__'
        # fields = ['name','page']
        # exclude = ['price']
        # 错误信息,外层字典中的key是对应的字段名,value时字段名对应的错误信息的内层字典,
        # 内层字典的key时对应错误类型,value对应错误信息,即发生相应错误时需要显示的信息。
        error_messages = {
            'page': {
                'required':'页数不能为空!',
                'invalid': '请输入一个可用的page参数'
            },
            'title':{
                'max_length':'name不能超过100个字符',
            },
            'price':{
                'max_value':'图书价格不能超过1000元',
            }
        }

这样我们就自定义好了我们的自定义返回的错误信息。

在views中的视图函数中,我们想要将数据存储到数据库中去,是不是需要将所有的数据都取出来,然后在调用模型的save方法或者create方法才能存储进去。如果我们使用的表单验证器,那么我们就不需要要将数据取出来在存入数据库了,直接调用表单的save方法就可以了,示例:修改视图中的代码:

class add_book(View):
    def get(self,request):
        return render(request,'add_book.html')
    def post(self,request):
        form = AddBookForm(request.POST)
        if form.is_valid():
            # name = form.cleaned_data.get('name')
            # page = form.cleaned_data.get('page')
            # price = form.cleaned_data.get('price')
            # print(name,page,price)
            form.save()
            return HttpResponse('success')
        else:
            print(form.errors.get_json_data())
            return HttpResponse('Fail')

==注意: == 使用save方法表单中的fields属性必须为
‘__all __’,否则会报错。

那么如果我们在表单中只选取了某些字段,那么我们应该怎样操作呢?

这个时候我们就可以在save中传入一个参数,commit=false

例如,当我们在实现注册页面的时候,前端需要传入两个密码pwd1和pwd2,只有两个密码相等时才可以进行成功注册。

那么我们这个时候使用form.save()方法肯定是会报错的。

这个时候我们就需要使用到commit=False
参数了。

示例,为了和上面的RegisterForm这个验证器视图形成对比,我们在forms中新建一个RegisterModelForm的验证器视图:

class RegisterModelForm(forms.ModelForm):
    # 因为在Meta中我们只有name和telephone属性,所以我们需要自定义两个pwd属性来接收前端传入的值
    pwd1 = forms.CharField(max_length=16, min_length=6,error_messages={'required': '密码不能为空', 'min_length': '密码最少不能少于6个字符','max_length': '密码最多不能超过16个字符'})  # 第一次输入的密码
    pwd2 = forms.CharField(max_length=16, min_length=6,error_messages={'required': '确认密码不能为空', 'min_length': '密码最少不能少于6个字符','max_length': '密码最多不能超过16个字符'})  # 第二次输入的密码

    def clean(self):
        cleaned_data = super().clean()
        pwd1 = cleaned_data.get('pwd1')
        pwd2 = cleaned_data.get('pwd2')
        if pwd1 != pwd2:
            raise forms.ValidationError('两次输入密码不一致~~')
        return cleaned_data

    class Meta:
        model = models.User
        fields = ['name','telephone']

        error_messages = {
            'name': {
                'required': 'username不能为空!',
            },
            'telephone': {
                'required': 'telephone不能为空!',
                'invalid': 'telephone必须为11个数字',
            },
        }

然后views中也是一样,我们定义一个RegisterModelFormView的视图,能和前面的RegisterView形成对比,

from .forms import MessageBoardForm,MyForm,RegisterForm,AddBookForm,RegisterModelForm


class RegisterModelFormView(View):
    def get(self,request):
        return render(request,'register.html')
    def post(self,request):
        form = RegisterModelForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)
            user.pwd = form.cleaned_data.get('pwd1')
            user.save()
            return HttpResponse('success')
        else:
            print(form.errors.get_json_data())
            return HttpResponse('Fail')

这里我们渲染的还时前面RegisterView中的Html模板,但是在前面我们定义的name是用username来传入数据的,但是我们现在是使用的form.Models在表单验证器中自动生成的字段,而在user模型中的字段为name,所以表单验证器中的字段也为name,所以我们是获取不到name的数据的,所以我们需要修改一下前端的代码

<form action="" method="post">
    <table>
        <tbody>
            <tr>
                <td>用户名:</td>
{#                这是RegisterForm使用的#}
{#                <td><input type="text" name="username"></td>#}
{#                这是RegisterModelForm使用的#}
                <td><input type="text" name="name"></td>
                {% if errors.username %}
                    <td><p>{{ errors.username }}</p></td>
                {% endif %}
            </tr>
            <tr>
                <td>电话号码:</td>
                <td><input type="text" name="telephone"></td>
                {% if errors.telephone %}
                    <td><p>{{ errors.telephone }}</p></td>
                {% endif %}
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd1"></td>
                {% if errors.pwd1 %}
                    <td><p>{{ errors.pwd1 }}</p></td>
                {% endif %}
            </tr>
            <tr>
                <td>确认密码:</td>
                <td><input type="password" name="pwd2"></td>
                {% if errors.pwd2 %}
                    <td><p>{{ errors.pwd2 }}</p></td>
                {% endif %}
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </tbody>
    </table>
</form>

然后在urls中添加映射,就实现了我们的需求。

注意: 在调用save方法的时候,如果传入一个commit=False,那么只会生成这个模型的对象,而不会把这个对象真正的插入到数据库中。比如上面一样。

想深入学习django的可以看一下这个视频:超详细讲解Django打造大型企业官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值