当前位置: 首页 > news >正文

Django5.1(130)—— 表单 API一(API参考)

表单 API一


关于此文档

本文档介绍了 Django 的表单 API 的具体细节。

绑定和非绑定表单

一个 Form 实例要么是 绑定 到一组数据,要么是 未绑定

  • 如果是 绑定 了一组数据,它就能够验证这些数据,并将表单渲染成 HTML,并在 HTML 中显示数据。
  • 如果是 未绑定,它就不能进行验证(因为没有数据可验证!),但它仍然可以将空白表单渲染为 HTML。

class Form[源代码]

要创建一个未绑定的 Form 实例,实例化这个类:

>>> f = ContactForm()

要将数据绑定到表单,将数据作为字典传递给 Form 类构造函数的第一个参数:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)

    在这个字典中,键是字段名,对应于 Form 类中的属性。值是你要验证的数据。这些数据通常是字符串,但不要求它们是字符串;你传递的数据类型取决于 Field,我们稍后会看到。

    Form.is_bound

    如果在运行时需要区分绑定和未绑定的表单实例,请检查表单的 is_bound 属性的值:

    >>> f = ContactForm()
    >>> f.is_bound
    False
    >>> f = ContactForm({"subject": "hello"})
    >>> f.is_bound
    True

    请注意,传递一个空字典会创建一个带有空数据的 绑定 表单:

    >>> f = ContactForm({})
    >>> f.is_bound
    True

      如果你有一个绑定的 Form 实例,并想以某种方式改变数据,或者你想将一个未绑定的 Form 实例绑定到一些数据,请创建另一个 Form 实例。没有办法改变一个 Form 实例中的数据。一旦创建了一个 Form 实例,你应该认为它的数据是不可改变的,不管它是否有数据。

      使用表单来验证数据


      Form.clean()

      当你必须为相互依赖的字段添加自定义验证时,在你的 Form 上实现一个 clean() 方法。

      Form.is_valid()

      Form 对象的主要任务是验证数据。对于一个绑定的 Form 实例,调用 is_valid() 方法来运行验证,并返回一个布尔值,表示数据是否有效:

      >>> data = {
      ...     "subject": "hello",
      ...     "message": "Hi there",
      ...     "sender": "foo@example.com",
      ...     "cc_myself": True,
      ... }
      >>> f = ContactForm(data)
      >>> f.is_valid()
      True

      让我们尝试一些无效的数据。在这种情况下,subject 是空白的(这是一个错误,因为默认情况下所有字段都是必填的),而 sender 不是一个有效的电子邮件地址:

      >>> data = {
      ...     "subject": "",
      ...     "message": "Hi there",
      ...     "sender": "invalid email address",
      ...     "cc_myself": True,
      ... }
      >>> f = ContactForm(data)
      >>> f.is_valid()
      False

        Form.errors

        访问 errors 属性以获取错误消息的字典:

        >>> f.errors
        {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

        在这个字典中,键是字段名,值是代表错误信息的字符串列表。错误信息存储在列表中,因为一个字段可以有多个错误信息。

        你可以访问 errors,而不必先调用 is_valid()。无论是调用 is_valid() 还是访问 errors,表单的数据都会首先被验证。

        验证例程只会被调用一次,无论你访问 errors 或调用 is_valid() 多少次。这意味着,如果验证有副作用,这些副作用将只被触发一次。

        Form.errors.as_data()

        返回一个 dict,将字段映射到它们的原始 ValidationError 实例。

        >>> f.errors.as_data()
        {'sender': [ValidationError(['Enter a valid email address.'])],
        'subject': [ValidationError(['This field is required.'])]}

        当你需要通过错误 code 来识别错误时,请使用此方法。这样就可以在给定的错误出现时,重写错误信息或在视图中编写自定义逻辑。它还可以用来以自定义格式(如 XML)将错误序列化;例如, as_json() 依赖于 as_data()

        需要使用 as_data() 方法是由于向后兼容性。以前,ValidationError 实例一旦被添加到 Form.errors 字典中,其 渲染的 错误信息就会丢失。理想情况下,Form.errors 会存储 ValidationError 实例,并且带有 as_ 前缀的方法可以渲染它们,但为了不破坏那些期望在 Form.errors 中渲染错误信息的代码,必须反过来做。

        Form.errors.as_json(escape_html=False)

        返回以 JSON 格式序列化的错误。

        >>> f.errors.as_json()
        {"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
        "subject": [{"message": "This field is required.", "code": "required"}]}

        默认情况下,as_json() 不会转义其输出。如果你使用它来处理类似 AJAX 请求的表单视图,客户端解释响应并将错误插入到页面中,你会希望确保在客户端转义结果,以避免跨站点脚本攻击的可能性。你可以在 JavaScript 中使用 element.textContent = errorText 或者使用 jQuery 的 $(el).text(errorText) (而不是它的 .html() 函数)来实现。

        如果出于某些原因你不想使用客户端转义,你也可以设置 escape_html=True,错误信息将被转义,这样你就可以直接在 HTML 中使用它们。

        Form.errors.get_json_data(escape_html=False)

        Form. errors.as_json() 将返回序列化的 JSON,而这个则是返回序列化之前的错误数据。

        escape_html 参数的行为如 Form.errors.as_json() 中所述。

        Form.add_error(fielderror)

        该方法允许在 Form.clean() 方法中添加错误到特定字段,或者从表单外部添加错误,例如从视图中添加。

        field 参数是应该添加错误的字段名。如果它的值是 None,错误将被视为非字段错误,就像 Form.non_field_errors() 返回的那样。

        error 参数可以是一个字符串,或者最好是 ValidationError 的实例。关于定义表单错误的最佳做法。

        注意,Form.add_error() 会自动从 cleaned_data 中删除相关字段。

        Form.has_error(fieldcode=None)

        本方法返回一个布尔值,表示一个字段是否有特定错误 code 的错误。如果 code 是 None,如果字段包含任何错误,它将返回 True

        要检查非字段错误,使用 NON_FIELD_ERRORS 作为 field 参数。

        Form.non_field_errors()

        这个方法从 Form.errors 中返回没有与特定字段关联的错误列表。这包括在 Form.clean() 中引发的 ValidationError 和使用 Form.add_error(None, “…”) 添加的错误。

        非绑定表单的行为

        对于没有数据的表单进行验证是没有意义的,但是为了记录,以下是未绑定表单的情况:

        >>> f = ContactForm()
        >>> f.is_valid()
        False
        >>> f.errors
        {}

        初始表单值


        Form.initial

        使用 initial 在运行时声明表单字段的初始值。例如,你可能想用当前会话的用户名来填写 username 字段。

        要实现这一点,可以使用 initial 参数来设置 Form 的初始值。如果提供了该参数,它应该是一个将字段名称映射到初始值的字典。只需要包括要指定初始值的字段,不需要包括表单中的每个字段。例如:

        >>> f = ContactForm(initial={"subject": "Hi there!"})

        这些值只对未绑定的表单显示,如果没有提供特定的值,它们不会被用作后备值。

        如果一个 Field 定义了 initial,并且在实例化表单时包括了 initial,那么后者的 initial 会具有优先权。在这个示例中,initial 在字段级别和表单实例级别都提供了,后者优先:

        >>> from django import forms
        >>> class CommentForm(forms.Form):
        ...     name = forms.CharField(initial="class")
        ...     url = forms.URLField()
        ...     comment = forms.CharField()
        ...
        >>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
        >>> print(f)
        <div>Name:<input type="text" name="name" value="instance" required></div>
        <div>Url:<input type="url" name="url" required></div>
        <div>Comment:<input type="text" name="comment" required></div>

        Form.get_initial_for_field(fieldfield_name)

        返回一个表单字段的初始数据。如果存在的话,它从 Form.initial 检索数据,否则尝试 Field.initial。可调用的值将被执行。

        建议使用 BoundField.initial 而不是 get_initial_for_field(),因为 BoundField.initial 具有更简单的接口。而且,与 get_initial_for_field() 不同,BoundField.initial 缓存其值。这在处理返回值可能会变化的可调用对象时特别有用(例如 datetime.now 或 uuid.uuid4):

        >>> import uuid
        >>> class UUIDCommentForm(CommentForm):
        ...     identifier = forms.UUIDField(initial=uuid.uuid4)
        ...
        >>> f = UUIDCommentForm()
        >>> f.get_initial_for_field(f.fields["identifier"], "identifier")
        UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
        >>> f.get_initial_for_field(f.fields["identifier"], "identifier")
        UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
        >>> # Using BoundField.initial, for comparison
        >>> f["identifier"].initial
        UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
        >>> f["identifier"].initial
        UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')

        检查哪些表格数据已经改变

        Form.has_changed()

        当你需要检查表单数据是否与初始数据发生变化时,请使用 has_changed() 方法。

        >>> data = {
        ...     "subject": "hello",
        ...     "message": "Hi there",
        ...     "sender": "foo@example.com",
        ...     "cc_myself": True,
        ... }
        >>> f = ContactForm(data, initial=data)
        >>> f.has_changed()
        False

        当表单提交后,我们会重新构建表单,并提供原始数据,以便进行对比。

        >>> f = ContactForm(request.POST, initial=data)
        >>> f.has_changed()

          如果来自 request.POST 的数据与 initial 中提供的数据不同,那么 has_changed() 将为 True,否则为 False。结果是通过调用 Field.has_changed() 对表单中的每个字段进行计算。

          Form.changed_data

          changed_data 属性返回表单绑定数据(通常是 request.POST)中与 initial 中提供的数据不同的字段名列表。如果没有不同的数据,则返回一个空列表。

          >>> f = ContactForm(request.POST, initial=data)
          >>> if f.has_changed():
          ...     print("The following fields changed: %s" % ", ".join(f.changed_data))
          ...
          >>> f.changed_data
          ['subject', 'message']

          访问表单中的字段

          Form.fields

          您可以通过表单实例的 fields 属性访问 Form 的字段:

          >>> for row in f.fields.values():
          ...     print(row)
          ...
          <django.forms.fields.CharField object at 0x7ffaac632510>
          <django.forms.fields.URLField object at 0x7ffaac632f90>
          <django.forms.fields.CharField object at 0x7ffaac3aa050>
          >>> f.fields["name"]
          <django.forms.fields.CharField object at 0x7ffaac6324d0>

          您可以更改 Form 实例的字段和 BoundField,以改变它在表单中的呈现方式:

          >>> f.as_div().split("</div>")[0]
          '<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
          >>> f["subject"].label = "Topic"
          >>> f.as_div().split("</div>")[0]
          '<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

          小心不要修改 base_fields 属性,因为这种修改会影响到同一个 Python 进程中所有后续的 ContactForm 实例:

          >>> f.base_fields["subject"].label_suffix = "?"
          >>> another_f = CommentForm(auto_id=False)
          >>> f.as_div().split("</div>")[0]
          '<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

            访问“干净的”数据

            Form.cleaned_data

            Form 类中的每个字段不仅负责验证数据,还负责“清理”数据——将其规范为一致的格式。这是一个很好的功能,因为它允许以各种方式输入特定字段的数据,并总是产生一致的输出。

            例如, DateField 将输入规范化为 Python 的 datetime.date 对象。无论你传递给它的是格式为 '1994-07-15' 的字符串,还是 datetime.date 对象,或者其他一些格式,只要它是有效的,DateField 都会将它规范化为 datetime.date 对象。

            一旦你创建了一个带有一组数据并进行了验证的 Form 实例,你可以通过它的 cleaned_data 属性访问清洁后的数据:

            >>> data = {
            ...     "subject": "hello",
            ...     "message": "Hi there",
            ...     "sender": "foo@example.com",
            ...     "cc_myself": True,
            ... }
            >>> f = ContactForm(data)
            >>> f.is_valid()
            True
            >>> f.cleaned_data
            {'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

            请注意,任何基于文本的字段——如 CharField 或 EmailField——总是将输入清理成一个字符串。我们将在本文档后面介绍编码的含义。

            如果你的数据验证不通过,cleaned_data 字典仅包含有效的字段:

            >>> data = {
            ...     "subject": "",
            ...     "message": "Hi there",
            ...     "sender": "invalid email address",
            ...     "cc_myself": True,
            ... }
            >>> f = ContactForm(data)
            >>> f.is_valid()
            False
            >>> f.cleaned_data
            {'cc_myself': True, 'message': 'Hi there'}

            cleaned_data 将始终仅包含在 Form 中定义的字段的键,即使在定义 Form 时传递了额外的数据。在这个例子中,我们在 ContactForm 构造函数中传递了许多额外字段,但 cleaned_data 仅包含表单的字段:

            >>> data = {
            ...     "subject": "hello",
            ...     "message": "Hi there",
            ...     "sender": "foo@example.com",
            ...     "cc_myself": True,
            ...     "extra_field_1": "foo",
            ...     "extra_field_2": "bar",
            ...     "extra_field_3": "baz",
            ... }
            >>> f = ContactForm(data)
            >>> f.is_valid()
            True
            >>> f.cleaned_data  # Doesn't contain extra_field_1, etc.
            {'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

              当 Form 有效时,即使数据中没有为一些可选字段提供值,cleaned_data 也会包括所有字段的键和值。在这个例子中,数据字典没有包含 nick_name 字段的值,但 cleaned_data 包括它,并带有一个空值:

              >>> from django import forms
              >>> class OptionalPersonForm(forms.Form):
              ...     first_name = forms.CharField()
              ...     last_name = forms.CharField()
              ...     nick_name = forms.CharField(required=False)
              ...
              >>> data = {"first_name": "John", "last_name": "Lennon"}
              >>> f = OptionalPersonForm(data)
              >>> f.is_valid()
              True
              >>> f.cleaned_data
              {'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}

                在上面这个例子中,nick_name 的 cleaned_data 值被设置为一个空字符串,因为 nick_name 是 CharField,而 CharField 将空值视为空字符串。每个字段类型都知道它的“空”值是什么——例如,对于 DateField,它是 None 而不是空字符串。关于每个字段在这种情况下的行为的全部细节,请参阅下面“内置 Field 类”一节中每个字段的“空值”说明。

                你可以编写代码来对特定的表单字段(基于其名称)或整个表单(考虑各种字段的组合)进行验证。

                  http://www.lryc.cn/news/600496.html

                  相关文章:

                1. JavaScript里的reduce
                2. Android开发中协程工作原理解析
                3. # JsSIP 从入门到实战:构建你的第一个 Web 电话
                4. 数据结构 双向链表
                5. Spring Boot集成RabbitMQ终极指南:从配置到高级消息处理
                6. Vue 插槽
                7. Claude Code PowerShell 安装 MCPs 方法:以 Puppeteer 为例
                8. 如何实现打印功能
                9. AI 编程工具 Trae 重要的升级。。。
                10. Linux基本指令:掌握系统操作的钥匙
                11. 【Bluedroid】btif_av_sink_execute_service之服务器禁用源码流程解析
                12. 【架构师从入门到进阶】第五章:DNSCDN网关优化思路——第十节:网关安全-单向加密
                13. Item11:在operator=中处理自我赋值
                14. Go-Elasticsearch v9 安装与版本兼容性
                15. 全文检索官网示例
                16. “给予” 超越 “莲花”,支持图片在线编辑
                17. [论文阅读] 人工智能 + 软件工程 | NoCode-bench:评估LLM无代码功能添加能力的新基准
                18. SSRF_XXE_RCE_反序列化学习
                19. 面试实战,问题十三,Redis在Java项目中的作用及使用场景详解,怎么回答
                20. 大语言模型 LLM 通过 Excel 知识库 增强日志分析,根因分析能力的技术方案(3):使用云平台最小外部依赖方案
                21. GMP模型
                22. 深入解析Java内存模型:原理与并发优化实践
                23. Oracle 误删数据恢复
                24. ClickHouse高性能实时分析数据库-高性能的模式设计
                25. 学习随想录-- web3学习入门计划
                26. 50道JavaScript基础面试题:从基础到进阶
                27. haproxy原理及实战部署
                28. 根本是什么
                29. 统计学07:概率论基础
                30. Chukonu 阅读笔记