Loading

Django-Form-ModelForm

一、Form简介

Form主要用于对用户输入的input标签内容进行校验,比如校验用户是否输入,输入的长度和格式等正不正确,并且有错误就需要在页面上相应的位置显示对应的错误信息。

总结:

  • 生成页面可以用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

示例:使用form写简单注册界面

views.py 文件

from django.shortcuts import render, HttpResponse
from django import forms


class Register(forms.Form):
    name = forms.CharField(
        label='用户名:',       # 设置label值
        min_length=6,          # 最小6个字符
    )

    password = forms.CharField(
        label='密码:',
        min_length=6,       # 最小6个字符
        max_length=11,      # 最大11个字符
        # 设置input的type属性为password
        widget=forms.widgets.PasswordInput(attrs={'type': 'password'})
    )


def register(request):
    form_obj = Register()
    if request.method == 'GET':
        return render(request, 'register.html', {'form_obj': form_obj})
    else:
        # 实例化form对象,并把post提交过来的数据传递进去
        form_obj = Register(data=request.POST)
        # 调用form_obj校验数据
        if form_obj.is_valid():
            return HttpResponse('注册成功!!')
        return render(request, 'register.html', {'form_obj': form_obj})

register.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" novalidate method="post">
    {% csrf_token %}
    <div>
        <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
        {{ form_obj.name }}     <!-- 会渲染成input标签 -->
        <!-- 该字段的错误文本信息! -->
        <span style="color:red">{{ form_obj.name.errors.0 }}</span>
    </div>

    <div>
        <label for="{{ form_obj.password.id_for_label }}">{{ form_obj.password.label }}</label>
        {{ form_obj.password }}
        <span style="color: red">{{ form_obj.password.errors.0 }}</span>
    </div>
    <input type="submit">
</form>


</body>
</html>

二、Form常用字段

创建Form类时,主要涉及到【字段】和【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML。

initial

初始值,input框里面的初始值。

class Text(forms.Form):
    name = forms.CharField(
        label='用户名',
        initial='默认值',
    )

error_messages

重写错误信息

class Text(forms.Form):
    name = forms.CharField(
        label='用户名',
        initial='小杨',
        min_length=6,
        error_messages={
            'required': '不能为空',
            'invalid': '格式错误',
            'min_length': '用户名最短6位',
        }
    )

password

class Text(forms.Form):
    password = forms.CharField(
        min_length=6,
        label='密码',
        widget=forms.widgets.PasswordInput(
            attrs={
                'class': 'c1',          # 设置该标签class=c1
                'type': 'password',     # 设置该标签type=password
            }
        )
    )

RadioSelect

单选

class Text(forms.Form):
    sex = forms.ChoiceField(
        label='性别',
        initial=1,      # 默认选择男
        widget=forms.widgets.RadioSelect(),     # 单选
        choices=(
            (1, '男'),   # 1——>为提交的值
            (2, '女'),   # ’女‘——>为页面显示的值
        )
    )

单选Select

单选下拉框

class Text(forms.Form):

    sex = forms.ChoiceField(
        label='性别',
        initial=1,      # 默认选择男
        widget=forms.widgets.Select(),     # 插件为Select单选
        choices=(
            (1, '男'),   # 1——>为提交的值
            (2, '女'),   # ’女‘——>为页面显示的值
        )
    )

多选Select

多选下拉框

class Text(forms.Form):

    hobby = forms.MultipleChoiceField(   # 多选框的时候用MultipleChoiceField
        label='爱好',
        initial=1,      # 默认选择篮球
        widget=forms.widgets.SelectMultiple(),     # 对选插件为SelectMultiple
        choices=(
            (1, '篮球'),   # 1——>为提交的值
            (2, '跑步'),   # ’跑步‘——>为页面显示的值
            (3, '运动'),
            (4, '听歌'),
        )
    )

单选Checkbox

class Text(forms.Form):

    keep = forms.ChoiceField(
        label='是否7天自动登录',
        initial='1',        # 默认选中
        choices=(
            ('True', 1),    # 选中就为True
            ('False', 0),   # 没选中就为False
        ),
        widget=forms.widgets.CheckboxInput(),
    )

多选Checkbox

class Text(forms.Form):
    hobby = forms.MultipleChoiceField(
        label='爱好',
        initial=1,  # 默认选中
        choices=(
            (1, '篮球'),  # 1——>为提交的值
            (2, '跑步'),  # ’跑步‘——>为页面显示的值
            (3, '运动'),
            (4, '听歌'),
        ),
        widget=forms.widgets.CheckboxSelectMultiple(),
    )

date类型

date必须指定type

class Text(forms.Form):

    date = forms.DateField(
        initial='时间',
        widget=forms.widgets.TextInput(
            attrs={
                'type': 'date',     # 必须设置为date类型
            }
        )
    )

数据库动态获取数据构造标签

由于使用标签时,需要从配置的数据库中获取,但是上面所写的都是静态,无法实时更新,可以使用以下两种方法

方法一:重写init方法。但是*args和**kwargs一定要写上

from django.shortcuts import render
from django import forms
from app01 import models


class Text(forms.Form):

    city = forms.ChoiceField(
        initial=1,
        widget=forms.widgets.Select()
    )

    def __init__(self, *args, **kwargs):    # 必须加*args和**kwargs
        super().__init__(*args, **kwargs)
        # 把数据库查询的值赋值给city字段的choice里去
        self.fields['city'].choices = models.City.objects.all().values_list('id', 'name')
        print(self.fields['city'].choices)  # [(1, '上海'), (2, '成都'), (3, '重庆')]

方法二:

from django.shortcuts import render
from django import forms
from app01 import models


class Text(forms.Form):

    city = forms.ModelMultipleChoiceField(
        queryset=models.City.objects.all()
        """
        注意:
        	如果用这种方法,需要在City类表中加上__str__方法,不然显示的是一个个object对象
        """
    )

批量添加样式

通过重写Form类的init方法来实现

from django.shortcuts import render, HttpResponse
from django import forms


class Text(forms.Form):
    password = forms.CharField(
        label='密码',
        widget=forms.widgets.PasswordInput(attrs={
            'type': 'password',
        })
    )

    re_password = forms.CharField(
        label='确认密码',
        widget=forms.widgets.PasswordInput(attrs={
            'type': 'password',
        })
    )
    
    # 获取所有的字段self.fields循环添加
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update(
                {'class': 'c1'}
            )

三、字段校验

对前端提交的数据进行校验并返回错误。

RegexValidator验证器

可以写多个验证,以逗号分割

from django.shortcuts import render
from django import forms
from django.core.validators import RegexValidator	# 需要导入模块


class Text(forms.Form):
    count = forms.CharField(
        validators=[
            RegexValidator(r'^\d{4}$', '错误,只能是4位数字!'),
            RegexValidator(r'正则', '错误信息'),
        ]
    )

自定义验证函数

自定义验证规则的时候,如果不符合规则,需要自己发起错误

import re
from django.shortcuts import render, HttpResponse
from django import forms
from django.core.exceptions import ValidationError


def verify(value):
    value_re = re.compile(r'^\d{4}$')
    if not value_re.match(value):
        # 自定义验证需要自己发起错误
        raise ValidationError('错误,只能是4位数字')



class Text(forms.Form):
    count = forms.CharField(
        label='输入值:',
        validators=[verify, ],  # 调用自定义验证规则,可以调用多个。
    )

四、Hook钩子

在Form类中定义钩子函数,用来实现自定义的验证功能

局部钩子

在Form类中定义clean_字段名()方法,就能够实现对特定字段进行校验。

import re
from django.shortcuts import render
from django import forms
from django.core.exceptions import ValidationError


class Text(forms.Form):
    count = forms.CharField(
        label='值:',
        initial='123',
        error_messages={
            'required': '不能为空'
        }
    )

    
    # 局部钩子,对count字段进行验证
    def clean_count(self):
        value = self.cleaned_data.get('count')  # 获取count字段的值
        value_re = re.compile(r'^\d{4}$')       # 创建正则规则
        if not value_re.match(value):           # 判断带入值是否符合要求
            raise ValidationError('错误,只能是4位数字!')
        else:
            return value

全局钩子

在Form类中定义clean()方法,实现对字段进行全局校验。

示例:密码确认

class Text(forms.Form):
    password = forms.CharField(
        label='密码',
        widget=forms.widgets.PasswordInput(attrs={
            'type': 'password',
        })
    )

    re_password = forms.CharField(
        label='确认密码',
        widget=forms.widgets.PasswordInput(attrs={
            'type': 'password',
        })
    )

    # 执行全局钩子的时候,cleaned_data里面肯定是有了通过前面验证的所有数据
    def clean(self):
        password_value = self.cleaned_data.get('password')  # 获取password值
        re_password_value = self.cleaned_data.get('re_password')  # 获取re_password值
        if password_value == re_password_value:
            return self.cleaned_data  # 全局钩子要返回所有的数据
        else:
            # 在re_password字段增加这个错误,并且情况该字段值
            self.add_error('re_password', '两次密码不一致')
            raise ValidationError('两次密码不一致')

五、ModelForm

Django中大部分的表单都是与Django的模型有映射关系的。

因此Django提供了一个辅助类用来从Django的模型中创建Form,就是ModelForm。

ModelForm定义

ModelForm是Form和model的结合,会根据model的字段转换成对应的form字段,并且生成标签。

ModelForm通过原类meta里面的model对应的属性的表,去找它,并把表里面的属性filed全部转换成了form对应的filed

示例:

models.py 文件

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.CharField(max_length=4)

views.py 文件

from django.shortcuts import render
from django import forms
from app01 import models


class ModelForm_book(forms.ModelForm):
    # 定义一个元类
    class Meta():
        model = models.Book		# 获取model对应的属性的表
        fields = '__all__'		# 转换所有字段
        # 设置字段名labels
        labels = {
            'name': '书名',
            'publishDate': '出版日期',
            'price': '价格',
        }
        # 给字段加属性
        widgets = {
            'publishDate': forms.widgets.TextInput(attrs={'type': 'date'})
        }
        # 定义错误信息
        error_messages = {
            'name': {'required': '不能为空'}
        }


def book(request):
    # 获取数据
    book_obj = models.Book.objects.filter(pk=1).first()
    # 添加到modelform类中
    # 前端就会根据对应的数据默认显示
    book_model_form = ModelForm_book(instance=book_obj)
    return render(request, 'book.html', locals())

book.html 文件

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

<form action="" novalidate>
    {% for book in book_model_form %}
        <div>
            <label for="book.id_for_label">{{ book.label }}</label>
            {{ book }}
            {{ book.errors.0 }}
        </div>
    {% endfor %}
    <input type="submit">
</form>

</body>
</html>

class Meta: 常用参数

model = models.Book  	# 对应的Model中的类
fields = "__all__"  	# 字段,如果是__all__,就是表示列出所有的字段
exclude = None  		# 排除的字段
labels = None  			# 提示信息
help_texts = None  		# 帮助提示信息
widgets = None  		# 自定义插件
error_messages = None  	# 自定义错误信息

error_messages = {
    'title':{'required':'不能为空'}
}

save() 方法

save() 方法是每个ModelForm都具有的方法, 这个方法会根据表单绑定的数据, 创建并保存到对应的数据库中, 另外ModelForm类中接收instance参数----->(记录对象), 则传入的数据则会更新该记录对象, 如果没有提供save()则会创建一条记录

添加记录

class ModelForm_book(forms.ModelForm):
    # 定义一个元类
    class Meta:
        model = models.Book
        fields = '__all__'
        # 设置字段名labels
        labels = {
            'name': '书名',
            'publishDate': '出版日期',
            'price': '价格',
        }


def book(request):
    if request.method == 'GET':
        # 获取记录对象数据
        book_obj = models.Book.objects.filter(pk=1).first()
        # 添加到modelform类中
        # 前端就会根据对应的数据默认显示
        book_model_form = ModelForm_book(instance=book_obj)
        return render(request, 'book.html', locals())
    else:
        # 数据库中创建新记录
        book_obj = ModelForm_book(request.POST)  # 将post提交的数据带入ModelForm里
        if book_obj.is_valid():     # 数据校验
            book_obj.save()         # 创建新的记录
        return HttpResponse('ok')

更新记录: 传入需要更新的记录对象instance参数

def book(request):
    if request.method == 'GET':
        # 获取记录对象数据
        book_obj = models.Book.objects.filter(pk=1).first()
        # 添加到modelform类中
        # 前端就会根据对应的数据默认显示
        book_model_form = ModelForm_book(instance=book_obj)
        return render(request, 'book.html', locals())
    else:
        # 数据库中更新记录
        # 获取需要更新的记录
        update_book = models.Book.objects.filter(pk=1).first()
        # 将需要更新的记录和,更新的对象传入
        book_obj = ModelForm_book(request.POST, instance=update_book)  
        if book_obj.is_valid():     # 数据校验
            book_obj.save()         # 创建更新记录
        return HttpResponse('ok')
posted @ 2021-07-02 17:35  Mr-Yang`  阅读(112)  评论(0编辑  收藏  举报