Forms组件

基本使用

  • 导入forms组件
  • 定义一个类, 并继承Form
  • 在类中书写要校验的字段, 字段的属性就是要校验的规则
  • 实例化得到一个Form对象, 把要校验的数据传入
  • 调用[form对象].is_valid( )方法进行校验, 校验通过返回True
  • 校验通过调用[form对象].cleaned_data获得校验后的数据
  • 校验失败调用[form对象].errors获得错误信息

编写一个校验用户名和密码是否合法的功能

view.py

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


class MyForm(forms.Form):
    # 用户名最少三个字符最多八个字符
    user = forms.CharField(min_length=3, max_length=8, label='用户名',
                           error_messages={
                               'min_length': '用户名最短3位',
                               'max_length': '用户名最长8位',
                               'required': '用户名必填'
                           }
                           )
    # 用户名最小不能小于0,最大不能超过150
    age = forms.IntegerField(min_value=0, max_value=150, label='年龄',
                             error_messages={
                                 'min_value': '年龄最小0岁',
                                 'max_value': '年龄最大150岁',
                                 'required': '年龄必填'
                             }
                             )
    # 邮箱必须符合邮箱格式(关键符号@)
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱必填'
                             }
                             )


def index(request):
    # 1.先产生一个空对象
    form_obj = MyForm()
    if request.method == 'POST':
        # 2.获取用户数据
        form_obj = MyForm(request.POST)
        # 3.校验用户数据
        if form_obj.is_valid():
            print(form_obj.cleaned_data)
            return HttpResponse('数据没问题')
    return render(request, 'index.html', locals())

test.py(测试环境)

    from app01 import views

    # 1.将数据传入实例化对象
    form_obj = views.MyForm({'user': 'kevin', 'age': 22, 'email': '123'})
    # 2.查看数据是否合法(全部合法结果才是True)
    print(form_obj.is_valid())
    # 3.查看不符合条件的数据及原因
    print(form_obj.errors)
    # 4.查看符合条件的数据
    print(form_obj.cleaned_data)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="" method="post" novalidate>
        {% for form in form_obj %}
            <p>
            {{ form.label }}
            {{ form }}
            <span style="color: red">{{ form.errors.0 }}</span>
            </p>
        {% endfor %}
        
        <input type="submit">
    </form>
</body>
</html>

补充

  • forms类中所有的字段数据默认都是必填的,可以添加required=False字段就不必填写
  • forms类中额外传入的字段数据不会做任何的校验
  • forms组件只负责渲染获取用户数据的标签,form表单标签和提交按钮需要自己写
  • 渲染标签中文提示可以使用参数 label指定,不指定默认英文提醒
  • forms类中填写的校验性参数前端浏览器会识别并添加校验操作,但是前端的校验是可有可无的后端必须要再次校验
  • form表单可以取消浏览器自动添加校验功能的操作novalidate

钩子函数

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

局部钩子

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

views.py

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


class MyForm(forms.Form):
    name = forms.CharField(min_length=3, max_length=8, label='用户名',
                           error_messages={
                               'min_length': '用户名至少三位',
                               'max_length': '用户名最大八位',
                               'required': '用户名不能为空',
                           }
                           )
    # 局部钩子:效验用户名是否存在
    """钩子函数是数据经过了字段一层校验之后才会执行"""

    def clean_name(self):  # 自动生成的函数名,专门用于对name字段添加额外的校验规则

        name_list = ['kevin', 'jerry', 'tom']
        # 1.先获取用户名
        name = self.cleaned_data.get('name')
        # 2.判断用户名是否重复
        if name in name_list:
            # 3.提示信息
            self.add_error('name', '用户已存在')
        # 4.最后将勾上来的name返回回去
        return name

def index(request):
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            return HttpResponse('OK')
    return render(request, 'index.html', locals())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
    {% for foo in form_obj %}
        <p>{{ foo.label }}:{{ foo }}<span style="color: red">{{ foo.errors.0 }}</span></p>
    {% endfor %}
    <input type="submit">
</form>
</body>
</html>

全局钩子

在Fom类中定义 clean()方法,就能够实现对字段进行全局校验

views.py

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


class MyForm(forms.Form):
    password = forms.CharField(min_length=3, max_length=8, label='密码')
    confirm_password = forms.CharField(min_length=3, max_length=8, label='确认密码')

    # 全局钩子:校验密码与确认密码是否一致(一次性可以勾多个字段)
    def clean(self):
        # 1.获取多个字段数据
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        # 2.判断两次密码是否一致
        if not password == confirm_password:
            # 3.提示信息
            self.add_error('confirm_password', '两次密码不一致')
        # 4.最后将整个数据返回
        return self.cleaned_data


def index(request):
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            return HttpResponse('OK')
    return render(request, 'index.html', locals())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
    {% for foo in form_obj %}
        <p>{{ foo.label }}:{{ foo }}<span style="color: red">{{ foo.errors.0 }}</span></p>
    {% endfor %}
    <input type="submit">
</form>
</body>
</html>

forms组件字段参数

字段参数说明
min_length最小长度
max_length最大长度
label字段名称
error_messages错误提示
min_value最小值
max_value最大值
initial默认值
validators正则校验器
widget控制渲染出来的标签各项属性
help_text设置字段的描述文本

核心字段参数

required

给字段添加必填属性

initial

class MyForm(forms.Form):
        username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="kevin"  # 设置默认值
    )
    )

validators

指定一个列表,其中包含了为字段进行验证的函数

class MyForm(forms.Form):
    phone = forms.CharField(
        validators=[
            RegexValidator(r'^[0-9]+$', '请输入数字'),
            RegexValidator(r'^138[0-9]+$', '数字必须是138开头'),
        ]
    )

widget

指定渲染Widget时使用的widget类,也就是这个form字段在HTML页面中是显示为什么框

class MyForm(forms.Form):
    password = forms.CharField(
      widget=forms.widgets.PasswordInput(
        attrs={'class': 'form-control'}
      )
    )

disabled

设置有该属性的字段在前端页面中将显示为不可编辑状态

forms组件字段类型

1. BooleanField

  • 默认的Widget:CheckboxInput
  • 空值:False
  • 规范化为:Python的True或者False
  • 可用的错误信息键:required

2. CharField

  • 默认的Widget:TextInput
  • 空值:与empty_value给出的任何值。
  • 规范化为:一个Unicode 对象。
  • 验证max_lengthmin_length,如果设置了这两个参数。 否则,所有的输入都是合法的。
  • 可用的错误信息键:min_length, max_length, required

有四个可选参数:

  • max_length,min_length:设置字符串的最大和最小长度。
  • strip:如果True(默认),去除输入的前导和尾随空格。
  • empty_value:用来表示“空”的值。 默认为空字符串。

3. ChoiceField

  • 默认的Widget:Select
  • 空值:''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定的值是否在选项列表中。
  • 可用的错误信息键:required, invalid_choice

参数choices:用来作为该字段选项的一个二元组组成的可迭代对象(例如,列表或元组)或者一个可调用对象。格式与用于和ORM模型字段的choices参数相同。

4. TypedChoiceField

像ChoiceField一样,只是还有两个额外的参数:coerce和empty_value。

  • 默认的Widget:Select
  • 空值:empty_value参数设置的值。
  • 规范化为:coerce参数类型的值。
  • 验证给定的值在选项列表中存在并且可以被强制转换。
  • 可用的错误信息的键:required, invalid_choice

5. DateField

  • 默认的Widget:DateInput
  • 空值:None
  • 规范化为:datetime.date对象。
  • 验证给出的值是一个datetime.date、datetime.datetime 或指定日期格式的字符串。
  • 错误信息的键:required, invalid

接收一个可选的参数:input_formats。一个格式的列表,用于转换字符串为datetime.date对象。

如果没有提供input_formats,默认的输入格式为:

['%Y-%m-%d',      # '2006-10-25'
 '%m/%d/%Y',      # '10/25/2006'
 '%m/%d/%y']      # '10/25/06'

另外,如果你在设置中指定USE_L10N=False,以下的格式也将包含在默认的输入格式中:

['%b %d %Y',      # 'Oct 25 2006'
 '%b %d, %Y',     # 'Oct 25, 2006'
 '%d %b %Y',      # '25 Oct 2006'
 '%d %b, %Y',     # '25 Oct, 2006'
 '%B %d %Y',      # 'October 25 2006'
 '%B %d, %Y',     # 'October 25, 2006'
 '%d %B %Y',      # '25 October 2006'
 '%d %B, %Y']     # '25 October, 2006'

6. DateTimeField

  • 默认的Widget:DateTimeInput
  • 空值:None
  • 规范化为:Python的datetime.datetime对象。
  • 验证给出的值是一个datetime.datetime、datetime.date或指定日期格式的字符串。
  • 错误信息的键:required, invalid

接收一个可选的参数:input_formats

如果没有提供input_formats,默认的输入格式为:

['%Y-%m-%d %H:%M:%S',    # '2006-10-25 14:30:59'
 '%Y-%m-%d %H:%M',       # '2006-10-25 14:30'
 '%Y-%m-%d',             # '2006-10-25'
 '%m/%d/%Y %H:%M:%S',    # '10/25/2006 14:30:59'
 '%m/%d/%Y %H:%M',       # '10/25/2006 14:30'
 '%m/%d/%Y',             # '10/25/2006'
 '%m/%d/%y %H:%M:%S',    # '10/25/06 14:30:59'
 '%m/%d/%y %H:%M',       # '10/25/06 14:30'
 '%m/%d/%y']             # '10/25/06'

7. DecimalField

  • 默认的Widget:当Field.localize是False时为NumberInput,否则为TextInput。
  • 空值:None
  • 规范化为:Python decimal对象。
  • 验证给定的值为一个十进制数。 忽略前导和尾随的空白。
  • 错误信息的键:max_whole_digits, max_digits, max_decimal_places,max_value, invalid, required,min_value

接收四个可选的参数:

max_value,min_value:允许的值的范围,需要赋值decimal.Decimal对象,不能直接给个整数类型。

max_digits:值允许的最大位数(小数点之前和之后的数字总共的位数,前导的零将被删除)。

decimal_places:允许的最大小数位。

8. DurationField

  • 默认的Widget:TextInput
  • 空值:None
  • 规范化为:Python timedelta。
  • 验证给出的值是一个字符串,而且可以转换为timedelta对象。
  • 错误信息的键:required, invalid.

9. EmailField

  • 默认的Widget:EmailInput
  • 空值:''(一个空字符串)
  • 规范化为:Unicode 对象。
  • 使用正则表达式验证给出的值是一个合法的邮件地址。
  • 错误信息的键:required, invalid

两个可选的参数用于验证,max_length 和min_length。

10. FileField

  • 默认的Widget:ClearableFileInput
  • 空值:None
  • 规范化为:一个UploadedFile对象,它封装文件内容和文件名到一个对象内。
  • 验证非空的文件数据已经绑定到表单。
  • 错误信息的键:missing, invalid, required, empty, max_length

具有两个可选的参数用于验证:max_length 和 allow_empty_file。

11. FilePathField

  • 默认的Widget:Select
  • 空值:None
  • 规范化为:Unicode 对象。
  • 验证选择的选项在选项列表中存在。
  • 错误信息的键:required, invalid_choice

这个字段允许从一个特定的目录选择文件。 它有五个额外的参数,其中的path是必须的:

path:要列出的目录的绝对路径。 这个目录必须存在。

recursive:如果为False(默认值),只用直接位于path下的文件或目录作为选项。如果为True,将递归访问这个目录,其内所有的子目录和文件都将作为选项。

match:正则表达模式;只有具有与此表达式匹配的文件名称才被允许作为选项。

allow_files:可选。默认为True。表示是否应该包含指定位置的文件。它和allow_folders必须有一个为True。

allow_folders可选。默认为False。表示是否应该包含指定位置的目录。

12. FloatField

  • 默认的Widget:当Field.localize是False时为NumberInput,否则为TextInput。
  • 空值:None
  • 规范化为:Float 对象。
  • 验证给定的值是一个浮点数。
  • 错误信息的键:max_value, invalid, required, min_value

接收两个可选的参数用于验证,max_value和min_value,控制允许的值的范围。

13. ImageField

  • 默认的Widget:ClearableFileInput
  • 空值:None
  • 规范化为:一个UploadedFile 象,它封装文件内容和文件名为一个单独的对象。
  • 验证文件数据已绑定到表单,并且该文件是Pillow可以解析的图像格式。
  • 错误信息的键:missing, invalid, required, empty, invalid_image

使用ImageField需要安装Pillow(pip install pillow)。如果在上传图片时遇到图像损坏错误,通常意味着使用了Pillow不支持的格式。

>>> from PIL import Image
>>> from django import forms
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> class ImageForm(forms.Form):
...     img = forms.ImageField()
>>> file_data = {'img': SimpleUploadedFile('test.png', <file data>)}
>>> form = ImageForm({}, file_data)
# Pillow closes the underlying file descriptor.
>>> form.is_valid()
True
>>> image_field = form.cleaned_data['img']
>>> image_field.image
<PIL.PngImagePlugin.PngImageFile image mode=RGBA size=191x287 at 0x7F5985045C18>
>>> image_field.image.width
191
>>> image_field.image.height
287
>>> image_field.image.format
'PNG'
>>> image_field.image.getdata()
# Raises AttributeError: 'NoneType' object has no attribute 'seek'.
>>> image = Image.open(image_field)
>>> image.getdata()
<ImagingCore object at 0x7f5984f874b0>

14. IntegerField

  • 默认的Widget:当Field.localize是False时为NumberInput,否则为TextInput。
  • 空值:None
  • 规范化为:Python 整数或长整数。
  • 验证给定值是一个整数。 允许前导和尾随空格,类似Python的int()函数。
  • 错误信息的键:max_value, invalid, required, min_value

两个可选参数:max_value和min_value,控制允许的值的范围。

15.JSONField

Django3.1新增。

接收JSON编码的字段。

  • 默认的Widget:Textarea
  • 空值:None
  • 规范化为:一个JSON对象。
  • 验证给定值是否合法的JSON。
  • 错误信息的键:required, invalid
  • 可接受两个参数:encoder和decoder,编码器和解码器

16. GenericIPAddressField

包含IPv4或IPv6地址的字段。

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:一个Unicode对象。
  • 验证给定值是有效的IP地址。
  • 错误信息的键:required, invalid

有两个可选参数:protocol和unpack_ipv4

17. MultipleChoiceField

  • 默认的Widget:SelectMultiple
  • 空值:[](一个空列表)
  • 规范化为:一个Unicode 对象列表。
  • 验证给定值列表中的每个值都存在于选择列表中。
  • 错误信息的键:invalid_list, invalid_choice, required

18. TypedMultipleChoiceField

类似MultipleChoiceField,除了需要两个额外的参数,coerce和empty_value。

  • 默认的Widget:SelectMultiple
  • 空值:empty_value
  • 规范化为:coerce参数提供的类型值列表。
  • 验证给定值存在于选项列表中并且可以强制。
  • 错误信息的键:required, invalid_choice

19. NullBooleanField

  • 默认的Widget:NullBooleanSelect
  • 空值:None
  • 规范化为:Python None, False 或True 值。
  • 不验证任何内容(即,它从不引发ValidationError)。

20.RegexField

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定值与某个正则表达式匹配。
  • 错误信息的键:required, invalid

需要一个必需的参数:regex,需要匹配的正则表达式。

还可以接收max_length,min_length和strip参数,类似CharField。

21. SlugField

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定的字符串只包括字母、数字、下划线及连字符。
  • 错误信息的键:required, invalid

此字段用于在表单中表示模型的SlugField。

22. TimeField

  • 默认的Widget:TextInput
  • 空值:None
  • 规范化为:一个Python 的datetime.time 对象。
  • 验证给定值是datetime.time或以特定时间格式格式化的字符串。
  • 错误信息的键:required, invalid

接收一个可选的参数:input_formats,用于尝试将字符串转换为有效的datetime.time对象的格式列表。

如果没有提供input_formats,默认的输入格式为:

'%H:%M:%S',     # '14:30:59'
'%H:%M',        # '14:30'

23. URLField

  • 默认的Widget:URLInput
  • 空值:''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定值是个有效的URL。
  • 错误信息的键:required, invalid

可选参数:max_length和min_length

24. UUIDField

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:UUID对象。
  • 错误信息的键:required, invalid

25. ComboField

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:Unicode 对象。
  • 根据指定为ComboField的参数的每个字段验证给定值。
  • 错误信息的键:required, invalid

接收一个额外的必选参数:fields,用于验证字段值的字段列表(按提供它们的顺序)。

>>> from django.forms import ComboField
>>> f = ComboField(fields=[CharField(max_length=20), EmailField()])
>>> f.clean('test@example.com')
'test@example.com'
>>> f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
ValidationError: ['Ensure this value has at most 20 characters (it has 28).']

26. MultiValueField

  • 默认的Widget:TextInput
  • 空值:''(一个空字符串)
  • 规范化为:子类的compress方法返回的类型。
  • 根据指定为MultiValueField的参数的每个字段验证给定值。
  • 错误信息的键:incomplete, invalid, required

27. SplitDateTimeField

  • 默认的Widget:SplitDateTimeWidget
  • 空值:None
  • 规范化为:Python datetime.datetime 对象。
  • 验证给定的值是datetime.datetime或以特定日期时间格式格式化的字符串。
  • 错误信息的键:invalid_date, invalid, required, invalid_time

forms组件源码分析

为什么局部钩子要写成`clean_字段名?

is_valid()进行源码分析

    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not self.errors

self.is_bound(可以点击看一下)是只要传了数据data就一定true,着重点放在self.errors点击查看源码

    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

点击_errors查看默认是None,所以肯定会走self.full_clean()方法,查看源码

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self.cleaned_data.
        """
        self._errors = ErrorDict  # 继承字典用来存放不符合的字段
        if not self.is_bound:  # 传入数据肯定不走
            return
        self.cleaned_data = {}  # 符合的字段都放在这里
.
        if self.empty_permitted and not self.has_changed():  # self.empty_permitted 默认false 不会走,不用考虑(可以点击查看)
            return

        self._clean_fields()  # 局部钩子方法
        self._clean_form()  # 全局钩方法
        self._post_clean()  # 内部为pass

先查看self._clean_fields()(局部钩子执行位置 )

    def _clean_fields(self):
        for name, field in self.fields.items():  # 循环获取字段名和字段对象
        # fields是一个字典,forms组件的实例化就会自动创建一个fields
            if field.disabled:  # 如果这个字段禁用
                value = self.get_initial_for_field(field, name)
            else:
                  # 获取字段对应的用户数据,把用户上传的数据往字段里面填写校验
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):  # 是文件拿文件数据
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:  # 对传入的value进行校验
                    value = field.clean(value)
                self.cleaned_data[name] = value  # 把校验后的数据放到cleaned_data
                if hasattr(self, 'clean_%s' % name):  # 判断有没有局部钩子
                    value = getattr(self, 'clean_%s' % name)()  # 执行局部钩子
                    self.cleaned_data[name] = value  # 校验通过,把数据替换一下
            except ValidationError as e:  # 如果校验不通过,会抛异常,会被捕获
                self.add_error(name, e)   # 捕获后执行,添加到错误信息,errors是个列表,错误可能有多个

点击 add_error 查看关键信息

def add_error(self, field, error):
    ......
        ......
        if field in self.cleaned_data:  # 如果field字段对象在cleaned_data
            del self.cleaned_data[field]  # 那么就将其从中删除

查看源码发现校验数据的整个过程内部都有异常处理机制

把上面局部钩子不写self.add_error,直接主动报错

            # 主动报错
            from django.core.exceptions import ValidationError
            raise ValidationError('用户名已存在!!!!!!!!!')
class MyForm(forms.Form):
    name = forms.CharField(min_length=3, max_length=8, label='用户名',
                           error_messages={
                               'min_length': '用户名至少三位',
                               'max_length': '用户名最大八位',
                               'required': '用户名不能为空',
                           }
                           )
    # 局部钩子:效验用户名是否存在
    """钩子函数是数据经过了字段一层校验之后才会执行"""

    def clean_name(self):  # 自动生成的函数名,专门用于对name字段添加额外的校验规则

        name_list = ['kevin', 'jerry', 'tom']
        # 1.先获取用户名
        name = self.cleaned_data.get('name')
        # 2.判断用户名是否重复
        if name in name_list:
            # 3.提示信息
            # self.add_error('name', '用户已存在')

            # 主动报错
            from django.core.exceptions import ValidationError
            raise ValidationError('用户名已存在!!!!!!!!!')

        # 4.最后将勾上来的name返回回去
        return name

再看self._clean_form()(全局钩子执行位置)

    def _clean_form(self):
        try:
            cleaned_data = self.clean()  # 执行全局钩子并拿到返回值(父类中有clea()方法,如果你自己写了则执行你写的)
        except ValidationError as e:
            self.add_error(None, e)  # key作为None就是__all__
        else:
            if cleaned_data is not None:  # 没出错则判断全局钩子的返回值是否为空
                self.cleaned_data = cleaned_data  # 不是空则原封不动的返回给elf.cleaned_data

所以在全局钩子可以看到最后要返回cleaned_data,因为我们重写了这个方法

下面的_post_clean是一些可以自己写的功能接口,重新具备一定的功能

ModelForm组件

ModelForm(基于forms组件)比form减少代码代码冗余

models.py

class User(models.Model):
    name = models.CharField(max_length=32,)
    password = models.CharField(max_length=32)

views.py

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


class MyUser(forms.ModelForm):
    class Meta:
        model = models.User  # 指定关联的表
        fields = '__all__'  # 所有的字段全部生成对应的forms字段
        # exclude = ('name',)  # 排除的字段
        labels = {
            'name': '用户名',
            'password': '密码',
        }
        widgets = {
            'name': forms.widgets.TextInput(attrs={'class': 'form-control'}),
            'password': forms.widgets.PasswordInput(attrs={'class': 'form-control'}),
        }


def index(request):
    form_obj = MyUser()
    if request.method == 'POST':
        form_obj = MyUser(request.POST)
        if form_obj.is_valid():
            edit_obj = models.User.objects.filter(pk=1).first()
            form_obj = MyUser(request.POST, instance=edit_obj)  # 新增还是保存,取决于instance 参数,没有则新增
            form_obj.save()  # 编辑并保存
    return render(request, 'index.html', locals())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<form action="" method="post" novalidate style="max-width: 300px;margin: 10px auto" >
    {% for foo in form_obj %}
        <p>{{ foo.label }}:{{ foo }}<span style="color: red">{{ foo.errors.0 }}</span></p>
    {% endfor %}
    <input type="submit">
</form>
</body>
</html>
Last modification:May 29, 2022
如果觉得我的文章对你有用,请随意赞赏