在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回溯法实现数组全排列输出实例分析
Mar 17 Python
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
Nov 19 Python
Python脚本实现自动将数据库备份到 Dropbox
Feb 06 Python
python 生成器协程运算实例
Sep 04 Python
Python利用openpyxl库遍历Sheet的实例
May 03 Python
python简单操作excle的方法
Sep 12 Python
Django  ORM 练习题及答案
Jul 19 Python
python 解决print数组/矩阵无法完整输出的问题
Feb 19 Python
python GUI库图形界面开发之PyQt5信号与槽基础使用方法与实例
Mar 06 Python
Python依赖包迁移到断网环境操作
Jul 13 Python
Django静态文件加载失败解决方案
Aug 26 Python
深度学习tensorflow基础mnist
Apr 14 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
php session处理的定制
2009/03/16 PHP
用PHP书写安全的脚本代码
2012/02/05 PHP
PHP处理Oracle的CLOB实例
2014/11/03 PHP
解决Laravel5.5下的toArray问题
2019/10/15 PHP
jQuery 1.0.2
2006/10/11 Javascript
根据分辩率调用不同的CSS.
2007/01/08 Javascript
初学JavaScript_03(ExtJs Grid的简单使用)
2008/10/02 Javascript
jQuery ui插件的使用方法代码实例
2013/05/08 Javascript
浅谈JavaScript函数节流
2014/12/09 Javascript
简单介绍JavaScript数据类型之隐式类型转换
2015/12/28 Javascript
微信小程序 首页制作简单实例
2017/04/07 Javascript
angular+ionic返回上一页并刷新页面
2017/08/08 Javascript
详细分析单线程JS执行问题
2017/11/22 Javascript
Vue 自定义动态组件实例详解
2018/03/28 Javascript
JS实现方形抽奖效果
2018/08/27 Javascript
解决vue打包css文件中背景图片的路径问题
2018/09/03 Javascript
JS前端知识点offset,scroll,client,冒泡,事件对象的应用整理总结
2019/06/27 Javascript
javascript合并两个数组最简单的实现方法
2019/09/14 Javascript
javascript实现的图片预览和上传功能示例【兼容IE 9】
2020/05/01 Javascript
[03:09]显微镜下的DOTA2第一期——带你走进华丽的DOTA2世界
2014/06/20 DOTA
Python实现的简单万年历例子分享
2014/04/25 Python
浅析python中的分片与截断序列
2016/08/09 Python
python3制作捧腹网段子页爬虫
2017/02/12 Python
python数字图像处理实现直方图与均衡化
2018/05/04 Python
一百多行python代码实现抢票助手
2018/09/25 Python
使用django-guardian实现django-admin的行级权限控制的方法
2018/10/30 Python
python小白学习包管理器pip安装
2020/06/09 Python
keras CNN卷积核可视化,热度图教程
2020/06/22 Python
在django中查询获取数据,get, filter,all(),values()操作
2020/08/09 Python
python实现计算图形面积
2021/02/22 Python
北大自主招生自荐信
2013/10/19 职场文书
2014年十八届四中全会思想汇报范文
2014/10/17 职场文书
资料员岗位职责范本
2015/04/13 职场文书
反邪教学习心得体会
2016/01/15 职场文书
80后创业总结的9条职场用人思想,记得收藏
2019/08/13 职场文书
详解JAVA的控制语句
2021/11/11 Java/Android