美文网首页
使用表单

使用表单

作者: 蝉时雨丶 | 来源:发表于2020-06-19 10:32 被阅读0次

    除非您计划搭建的网站和应用只发布内容且不接收访问者的输入,否则您就需要理解和使用表单。

    Django提供了一系列的工具和库来帮助您构建表单来接收网站访客的输入,然后处理以及响应这些输入。

    HTML表单

    在HTML中,表单是在 <form>...</form> 中的一些元素,它允许访客做一些类似输入文本、选择选项、操作对象或空间等动作,然后发送这些信息到服务端。

    一些表单界面元素(文本框或复选框)内置在HTML中。其他会更复杂些;像弹出日期选择或者允许您移动滑块或者操作控件,一般通过使用JavaScript,CSS以及HTML表单中的 <input> 元素来实现这些效果。

    和它的元素 <input> 一样,表单必须指定两样东西:

    • 何地:负责响应用户输入数据的URL地址
    • 如何:数据请求使用的HTTP方法。

    例如,Django admin的登录表单包含了一些 <input> 元素:用户名用 type="text" ,密码用 type="password" ,登录按钮用 type="submit" 。它还包含一些用户看不到的隐藏文本字段,Django用它们来决定下一步行为。

    它还告诉浏览器表单数据应该发往 <form> 的 action 属性指定的URL—— /admin/ ,并且应该使用它的 method 属性指定的HTTP方法—— post 。

    当 <input type="submit" value="Log in"> 元素被触发的时候,数据会发送到 /admin/ 。

    GET 和 POST

    处理表单时只会用到 GET 和 POST 两种HTTP方法。

    Django的登录表单使用 POST 方法传输数据,在这个方法中浏览器会封装表单数据,为了传输会进行编码,然后发送到服务端并接收它的响应。

    相比之下,GET 方法将提交的数据捆绑到一个字符串中,并用它来组成一个URL。该URL包含了数据要发送的地址以及一些键值对应的数据。如果您在Django文档中进行一次搜索,就会看到这点,它会生成一个形似 https://docs.djangoproject.com/search/?q=forms&release=1 的URL。

    GET 和 POST 通常用于不同的目的。

    任何可能用于更改系统状态的请求应该使用 POST —— 比如一个更改数据库的请求。GET 应该只被用于不会影响系统状态的请求。

    GET 方法也不适合密码表单,因为密码会出现在URL中,于是也会出现在浏览器的历史记录以及服务器的日志中,而且都是以纯文本的形式。它也不适合处理大量的数据或者二进制数据,比如一张图片。在WEB应用的管理表单中使用 GET 请求具有安全隐患:攻击者很容易通过模拟请求来访问系统的敏感数据。 POST 方法通过与其他像Django的 CSRF protection 这样的保护措施配合使用,能对访问提供更多的控制。

    另一方面, GET 方法适用于诸如网页搜索表单这样的内容,因为这类呈现为一个 GET 请求的URL很容易被存为书签、分享或重新提交。

    Django在表单中的角色

    处理表单是一件挺复杂的事情。想想看Django的admin,许多不同类型的数据可能需要在一张表单中准备显示,渲染成HTML,使用方便的界面进行编辑,传到服务器,验证和清理数据,然后保存或跳过进行下一步处理。

    Django的表单功能可以简化和自动化上述工作的大部分内容,并且也能比大多数程序员自己编写代码去实现来的更安全些。

    Django会处理涉及表单的三个不同部分:

    • 准备并重组数据,以便下一步的渲染
    • 为数据创建HTML 表单
    • 接收并处理客户端提交的表单及数据

    您 可以 手动编写代码来实现,但Django 可以帮你完成所有这些工作。

    Django 中的表单

    我们已经简单的描述过了HTML 表单,但是一个HTML <form> 只是其所需的一部分。

    Web应用中所说的'表单',可能指的是HTML <form> ,或者是生成了它的Django Form ,再或者是提交时返回的结构化数据,亦或是这些端到端作业的合集。

    Django的 Form

    Django表单系统的核心组件是 Form 类。它与Django模型描述对象的逻辑结构、行为以及它呈现给我们内容的形式的方式大致相同, Form 类描述一张表单并决定它如何工作及呈现。

    类似于模型类的字段映射到数据库字段的方式,表单类的字段会映射到HTML表单的 <input> 元素。 ModelForm 通过 Form 映射模型类的字段到HTML表单的 <input> 元素,Django admin就基于此。

    表单字段本身也是类;他们管理表单数据并在提交表单时执行验证。 DateFieldFileField 处理的数据类型差别很大,所以必须用来处理不同的字段。

    在浏览器中,表单字段以HTML“控件”(用户界面的一个片段)的形式展现给我们。每个字段类型都有与之相匹配的 控件类 ,但必要时可以覆盖。

    实例化、处理和渲染表单

    在Django中渲染一个对象的时候,我们通常:

    1.在视图中获取它(例如从数据库中取出)
    2.将它传递给模板上下文
    3.使用模板变量将它扩展为HTML标记
    在模板中渲染表单几乎与渲染任何其他类型的对象的一样,但是存在一些关键性的差异。

    如果模型实例不包含数据,在模板中对它做任何处理几乎没什么用。但完全有理由用来渲染一张空表单——当我们希望用户来填充的时候就会这么做。

    所以当我们在视图中处理模型实例时,我们一般从数据库中获取它。当我们处理表单时,我们一般在视图中实例化它。

    当我们实例化表单时,我们可以选择让它为空或者对它预先填充,例如使用:

    来自已保存的模型实例的数据(例如在管理编辑表单的情况下)
    我们从其他来源获取的数据
    从前面一个HTML 表单提交过来的数据
    最后一种情况最有趣,因为这使得用户不仅可以阅读网站,而且还可以将信息发回给它。

    构建一张表单

    需要完成的工作

    假设您希望在您的网站上创建一张简易的表单,用来获取用户的名字。您需要在模板中使用类似代码:

    <form action="/your-name/" method="post">
        <label for="your_name">Your name: </label>
        <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
        <input type="submit" value="OK">
    </form>
    

    这告诉浏览器将表单数据返回给URL /your-name/ ,并使用 POST 方法。它将显示一个标签为 "Your name:" 的文本字段,以及一个 "OK" 按钮。如果模板上下文包含一个 current_name 变量,它会被预填充到 your_name 字段。

    您需要一个视图来渲染这个包含HTML表单的模板,并能适当提供 current_name 字段。

    提交表单时,发送给服务器的 POST 请求将包含表单数据。

    现在,您还需要一个与该 /your-name/ URL相对应的视图,该视图将在请求中找到相应的键/值对,然后对其进行处理。

    这是一张非常简单的表单。实际应用中,一张表单可能包含数十上百的字段,其中许多可能需要预填充,并且我们可能希望用户在结束操作前需要多次来回编辑-提交。

    我们可能需要在浏览器中进行一些验证,甚至在表单提交之前;我们可能希望使用更复杂的字段 ,以允许用户做类似日期选择等操作。

    此刻,我们很容易通过使用Django来完成以上大部分工作。

    在Django 中构建一张表单

    Form

    我们已经很清楚想要的HTML表单看起来会是什么样子。首先,在Django中这样做:

    forms.py

    from django import forms
    
    class NameForm(forms.Form):
        your_name = forms.CharField(label='Your name', max_length=100)
    

    它定义了一个只包含一个字段( your_name )的 Form 类。我们已经为这个字段提供了友好的标签,当它渲染后会显示在 <label> 中(在这种情况下,如果我们省略之前指定的 label ,它还是会自动生成一个一样的标签)。

    字段的最大长度由 max_length 来定义。它做了两件事情。首先它在HTML的 <input> 上增加了 maxlength="100" (这样浏览器会在第一时间阻止用户输入超过这个数量的字符串)。其次它还会在Django收到浏览器传过来的表单时,对数据长度进行验证(也就是服务器端验证)。

    A Form instance has an is_valid() method, which runs validation routines for all its fields. When this method is called, if all fields contain valid data, it will:

    • 返回 True
    • 将表单的数据放到它的属性 cleaned_data 中。

    这样整个表单在第一次渲染时,会显示如下:

    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" maxlength="100" required>
    

    注意它 没有 包含 <form> 标签和提交按钮。我们必须自己在模板中提供。

    视图

    发回Django网站的表单数据由视图来处理,一般和发布这个表单用的是同一个视图。这允许我们重用一些相同的逻辑。

    为了处理表单,我们需要将它实例化到我们希望发布的URL的对应的视图中:
    views.py

    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    
    from .forms import NameForm
    
    def get_name(request):
        # if this is a POST request we need to process the form data
        if request.method == 'POST':
            # create a form instance and populate it with data from the request:
            form = NameForm(request.POST)
            # check whether it's valid:
            if form.is_valid():
                # process the data in form.cleaned_data as required
                # ...
                # redirect to a new URL:
                return HttpResponseRedirect('/thanks/')
    
        # if a GET (or any other method) we'll create a blank form
        else:
            form = NameForm()
    
        return render(request, 'name.html', {'form': form})
    

    模板

    我们没有必要在模板 name.html 中做过多的操作:

    <form action="/your-name/" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="Submit">
    </form>
    

    所有的表单字段及其属性都将通过Django模板语言从 {{ form }} 中被解包成HTML标记。

    所有的表单字段及其属性都将通过Django模板语言从 {{ form }} 中被解包成HTML标记。

    • 表格和跨站请求伪造保护

    Django自带一个简单易用的 跨站请求伪造防护 。当通过 POST 方法提交一张启用了CSRF防护的表单时,您必须使用上例中这样的模板标签 csrf_token 。但是,由于CSRF防护在模板中没有与表单直接绑定,因此这个标签在本页文档之后的示例中都将被忽略。

    *HTML5输入类型和浏览器验证

    如果您的表单包含 URLFieldEmailField 或者其他整数字段类型,Django将使用 urlemailnumber HTML5输入类型。默认情况下,浏览器可能会在这些字段上应用他们自己的验证,这也许比Django的验证更加严格。如果您想禁用这个行为,请在 form 标签上设置 novalidate 属性,或者在字段上指定一个不同的控件,比如 TextInput

    现在我们有了一个可以工作的web表单,它通过一张Django Form 描述,由一个视图来处理并渲染成一个HTML <form>

    以上是您入门需要了解的所有内容,但是表单框架提供了更多垂手可得的内容。一旦您理解了上述过程的基础知识,您应该再了解下表单系统的其他功能,然后学习更多的底层机制。

    详解Django Form

    模型和表单

    实际上,如果您的表单是要直接用来添加或编辑Django模型,用 ModelForm ,可以省时、省力、省代码,因为它会根据 Model 类构建一张对应字段及其属性的表单。

    字段详解

    考虑一下比我们上面的小示例更有用的一张表单,我们可以用它在个人网站上实现“联系我”的功能:

    forms.py

    from django import forms
    
    class ContactForm(forms.Form):
        subject = forms.CharField(max_length=100)
        message = forms.CharField(widget=forms.Textarea)
        sender = forms.EmailField()
        cc_myself = forms.BooleanField(required=False)
    

    我们之前的表单只用了一个 CharField 类型的字段 your_name 。在这个例子中,我们的表单有四个字段: subjectmessagesendercc_myself 。只用到三种可用的字段类型: CharFieldEmailFieldBooleanField ;完整的字段类型清单请参看 Form fields

    控件

    每个表单字段都有一个相对应的 控件类 ,这个控件类又有对应的HTML表单控件,比如 <input type="text">

    多数情况下,字段都有合适的默认控件。比如,默认情况下, CharField 有个 TextInput 控件,它会在HTML中生成一个 <input type="text"> 。如果您想要的是 <textarea> `` ,您要在定义表单字段的时候指定控件,就像我们对 ``message 字段那样处理。

    字段数据

    无论用表单提交了什么数据,一旦通过调用 is_valid() 验证成功( is_valid() 返回 True ),已验证的表单数据将被放到 form.cleaned_data 字典中。这里的数据已经很好的为你转化为Python类型。

    注解

    此时您依然能够直接从 request.POST 中访问到未验证的数据,但最好还是使用经验证的数据。

    在上面的联系表单示例中, cc_myself 会被转化成一个布尔值。同样的,字段 IntegerFieldFloatField 的值分别会被转化为Python的 intfloat 类型。

    下面例举了如何在视图中处理表单数据:
    views.py

    from django.core.mail import send_mail
    
    if form.is_valid():
        subject = form.cleaned_data['subject']
        message = form.cleaned_data['message']
        sender = form.cleaned_data['sender']
        cc_myself = form.cleaned_data['cc_myself']
    
        recipients = ['info@example.com']
        if cc_myself:
            recipients.append(sender)
    
        send_mail(subject, message, sender, recipients)
        return HttpResponseRedirect('/thanks/')
    

    有些字段类型需要一些额外的处理。例如,使用表单上传文件就要以不同的方式处理(它们可以从 request.FILES 获取,而不是 request.POST 中)。有关如何使用表单处理文件上传的详细信息,请参见 Binding uploaded files to a form

    相关文章

      网友评论

          本文标题:使用表单

          本文链接:https://www.haomeiwen.com/subject/ykcvxktx.html