在Django框架中编写Contact表单的教程


Posted in Python onJuly 17, 2015

虽然我们一直使用书籍搜索的示例表单,并将起改进的很完美,但是这还是相当的简陋: 只包含一个字段,q。这简单的例子,我们不需要使用Django表单库来处理。 但是复杂一点的表单就需要多方面的处理,我们现在来一下一个较为复杂的例子: 站点联系表单。

这个表单包括用户提交的反馈信息,一个可选的e-mail回信地址。 当这个表单提交并且数据通过验证后,系统将自动发送一封包含题用户提交的信息的e-mail给站点工作人员。

我们从contact_form.html模板入手:

<html>
<head>
  <title>Contact us</title>
</head>
<body>
  <h1>Contact us</h1>

  {% if errors %}
    <ul>
      {% for error in errors %}
      <li>{{ error }}</li>
      {% endfor %}
    </ul>
  {% endif %}

  <form action="/contact/" method="post">
    <p>Subject: <input type="text" name="subject"></p>
    <p>Your e-mail (optional): <input type="text" name="email"></p>
    <p>Message: <textarea name="message" rows="10" cols="50"></textarea></p>
    <input type="submit" value="Submit">
  </form>
</body>
</html>

我们定义了三个字段: 主题,e-mail和反馈信息。 除了e-mail字段为可选,其他两个字段都是必填项。 注意,这里我们使用method=”post”而非method=”get”,因为这个表单会有一个服务器端的操作:发送一封e-mail。 并且,我们复制了前一个模板search_form.html中错误信息显示的代码。

如果我们顺着上一节编写search()视图的思路,那么一个contact()视图代码应该像这样:

from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response

def contact(request):
  errors = []
  if request.method == 'POST':
    if not request.POST.get('subject', ''):
      errors.append('Enter a subject.')
    if not request.POST.get('message', ''):
      errors.append('Enter a message.')
    if request.POST.get('email') and '@' not in request.POST['email']:
      errors.append('Enter a valid e-mail address.')
    if not errors:
      send_mail(
        request.POST['subject'],
        request.POST['message'],
        request.POST.get('email', 'noreply@example.com'),
        ['siteowner@example.com'],
      )
      return HttpResponseRedirect('/contact/thanks/')
  return render_to_response('contact_form.html',
    {'errors': errors})

(如果按照书中的示例做下来,这这里可能乎产生一个疑问:contact()视图是否要放在books/views.py这个文件里。 但是contact()视图与books应用没有任何关联,那么这个视图应该可以放在别的地方? 这毫无紧要,只要在URLconf里正确设置URL与视图之间的映射,Django会正确处理的。 笔者个人喜欢创建一个contact的文件夹,与books文件夹同级。这个文件夹中包括空的__init__.py和views.py两个文件。

现在来分析一下以上的代码:

    确认request.method的值是'POST'。用户浏览表单时这个值并不存在,当且仅当表单被提交时这个值才出现。 (在后面的例子中,request.method将会设置为'GET',因为在普通的网页浏览中,浏览器都使用GET,而非POST)。判断request.method的值很好地帮助我们将表单显示与表单处理隔离开来。

    我们使用request.POST代替request.GET来获取提交过来的数据。 这是必须的,因为contact_form.html里表单使用的是method=”post”。如果在视图里通过POST获取数据,那么request.GET将为空。

    这里,有两个必填项,subject 和 message,所以需要对这两个进行验证。 注意,我们使用request.POST.get()方法,并提供一个空的字符串作为默认值;这个方法很好的解决了键丢失与空数据问题。

    虽然email非必填项,但如果有提交她的值则我们也需进行验证。 我们的验证算法相当的薄弱,仅验证值是否包含@字符。 在实际应用中,需要更为健壮的验证机制(Django提供这些验证机制,稍候我们就会看到)。

    我们使用了django.core.mail.send_mail函数来发送e-mail。 这个函数有四个必选参数: 主题,正文,寄信人和收件人列表。 send_mail是Django的EmailMessage类的一个方便的包装,EmailMessage类提供了更高级的方法,比如附件,多部分邮件,以及对于邮件头部的完整控制。

    注意,若要使用send_mail()函数来发送邮件,那么服务器需要配置成能够对外发送邮件,并且在Django中设置出站服务器地址。 参见规范:http://docs.djangoproject.com/en/dev/topics/email/

    当邮件发送成功之后,我们使用HttpResponseRedirect对象将网页重定向至一个包含成功信息的页面。 包含成功信息的页面这里留给读者去编写(很简单 一个视图/URL映射/一份模板即可),但是我们要解释一下为何重定向至新的页面,而不是在模板中直接调用render_to_response()来输出。

    原因就是: 若用户刷新一个包含POST表单的页面,那么请求将会重新发送造成重复。 这通常会造成非期望的结果,比如说重复的数据库记录;在我们的例子中,将导致发送两封同样的邮件。 如果用户在POST表单之后被重定向至另外的页面,就不会造成重复的请求了。

    我们应每次都给成功的POST请求做重定向。 这就是web开发的最佳实践。

contact()视图可以正常工作,但是她的验证功能有些复杂。 想象一下假如一个表单包含一打字段,我们真的将必须去编写每个域对应的if判断语句?

另外一个问题是表单的重新显示。若数据验证失败后,返回客户端的表单中各字段最好是填有原来提交的数据,以便用户查看哪里出现错误(用户也不需再次填写正确的字段值)。 我们可以手动地将原来的提交数据返回给模板,并且必须编辑HTML里的各字段来填充原来的值。

# views.py

def contact(request):
  errors = []
  if request.method == 'POST':
    if not request.POST.get('subject', ''):
      errors.append('Enter a subject.')
    if not request.POST.get('message', ''):
      errors.append('Enter a message.')
    if request.POST.get('email') and '@' not in request.POST['email']:
      errors.append('Enter a valid e-mail address.')
    if not errors:
      send_mail(
        request.POST['subject'],
        request.POST['message'],
        request.POST.get('email', `'noreply@example.com`_'),
        [`'siteowner@example.com`_'],
      )
      return HttpResponseRedirect('/contact/thanks/')
  return render_to_response('contact_form.html', {
    'errors': errors,
    **'subject': request.POST.get('subject', ''),**
    **'message': request.POST.get('message', ''),**
    **'email': request.POST.get('email', ''),**
  })

# contact_form.html

<html>
<head>
  <title>Contact us</title>
</head>
<body>
  <h1>Contact us</h1>

  {% if errors %}
    <ul>
      {% for error in errors %}
      <li>{{ error }}</li>
      {% endfor %}
    </ul>
  {% endif %}

  <form action="/contact/" method="post">
    <p>Subject: <input type="text" name="subject" **value="{{ subject }}"** ></p>
    <p>Your e-mail (optional): <input type="text" name="email" **value="{{ email }}"** ></p>
    <p>Message: <textarea name="message" rows="10" cols="50">**{{ message }}**</textarea></p>
    <input type="submit" value="Submit">
  </form>
</body>
</html>

这看起来杂乱,且写的时候容易出错。 希望你开始明白使用高级库的用意——负责处理表单及相关校验任务。

Python 相关文章推荐
Python选择排序、冒泡排序、合并排序代码实例
Apr 10 Python
python处理图片之PIL模块简单使用方法
May 11 Python
Python3实现从文件中读取指定行的方法
May 22 Python
Python中的异常处理相关语句基础学习笔记
Jul 11 Python
利用python GDAL库读写geotiff格式的遥感影像方法
Nov 29 Python
python getpass模块用法及实例详解
Oct 07 Python
Pytoch之torchvision.transforms图像变换实例
Dec 30 Python
Python基础之列表常见操作经典实例详解
Feb 26 Python
浅析PyCharm 的初始设置(知道)
Oct 12 Python
python定时截屏实现
Nov 02 Python
python 基于opencv操作摄像头
Dec 24 Python
python使用scapy模块实现ARP扫描的过程
Jan 21 Python
简单解析Django框架中的表单验证
Jul 17 #Python
改进Django中的表单的简单方法
Jul 17 #Python
Python的Django框架中的表单处理示例
Jul 17 #Python
Python中max函数用法实例分析
Jul 17 #Python
详解Django中Request对象的相关用法
Jul 17 #Python
Python实现SVN的目录周期性备份实例
Jul 17 #Python
Python的Django框架中设置日期和字段可选的方法
Jul 17 #Python
You might like
利用 window_onload 实现select默认选择
2006/10/09 PHP
PHP中动态显示签名和ip原理
2007/03/28 PHP
Gregarius中文日期格式问题解决办法
2008/04/22 PHP
php 调用远程url的六种方法小结
2009/11/02 PHP
PHP实现字符串翻转功能的方法【递归与循环算法】
2017/11/03 PHP
splice slice区别
2006/10/09 Javascript
JQuery的html(data)方法与&amp;lt;script&amp;gt;脚本块的解决方法
2010/03/09 Javascript
有关javascript的性能优化 (repaint和reflow)
2013/04/12 Javascript
Jquery实现自定义弹窗示例
2014/03/12 Javascript
你可能不知道的JavaScript的new Function()方法
2014/04/17 Javascript
JavaScript中的Web worker多线程API研究
2014/12/06 Javascript
使用jQuery不判断浏览器高度解决iframe自适应高度问题
2014/12/16 Javascript
使用jQuery和Bootstrap实现多层、自适应模态窗口
2014/12/22 Javascript
jquery点击缩略图切换视频播放特效代码分享
2015/09/15 Javascript
jQuery实现区域打印功能代码详解
2016/06/17 Javascript
浅谈js内置对象Math的属性和方法(推荐)
2016/09/19 Javascript
关于angularJs指令的Scope(作用域)介绍
2016/10/25 Javascript
详解Node.js 命令行程序开发教程
2017/06/07 Javascript
js实现图片粘贴上传到服务器并展示的实例
2017/11/08 Javascript
D3.js实现拓扑图的示例代码
2018/06/30 Javascript
vue通过cookie获取用户登录信息的思路详解
2018/10/30 Javascript
PHP读取远程txt文档到数组并实现遍历
2020/08/25 Javascript
[07:25]DOTA2-DPC中国联赛2月5日Recap集锦
2021/03/11 DOTA
python类继承与子类实例初始化用法分析
2015/04/17 Python
python在windows下创建隐藏窗口子进程的方法
2015/06/04 Python
python使用arcpy.mapping模块批量出图
2017/03/06 Python
Python cookbook(数据结构与算法)从任意长度的可迭代对象中分解元素操作示例
2018/02/13 Python
使用Python3内置文档高效学习以及官方中文文档
2019/05/19 Python
python itchat实现调用微信接口的第三方模块方法
2019/06/11 Python
Python实现图片识别加翻译功能
2019/12/26 Python
python3.7调试的实例方法
2020/07/21 Python
Microsoft新加坡官方网站:购买微软最新软件和技术产品
2016/10/28 全球购物
介绍一下.NET构架下remoting和webservice
2014/05/08 面试题
户外拓展活动方案
2014/02/11 职场文书
创建服务型党组织实施方案
2014/02/25 职场文书
2016年秋季趣味运动会开幕词
2016/03/04 职场文书