在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脚本实现DNSPod DNS动态解析域名
Feb 14 Python
python实现自动更换ip的方法
May 05 Python
Python编程argparse入门浅析
Feb 07 Python
python3爬取各类天气信息
Feb 24 Python
Java实现的执行python脚本工具类示例【使用jython.jar】
Mar 29 Python
基于python框架Scrapy爬取自己的博客内容过程详解
Aug 05 Python
python随机数分布random均匀分布实例
Nov 27 Python
Spring实战之使用util:命名空间简化配置操作示例
Dec 09 Python
Python3.7实现验证码登录方式代码实例
Feb 14 Python
pandas DataFrame 数据选取,修改,切片的实现
Apr 24 Python
浅析Python 条件控制语句
Jul 15 Python
Pandas的数据过滤实现
Jan 15 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 无限极分类
2008/03/27 PHP
php中定义网站根目录的常用方法
2010/08/08 PHP
CodeIgniter 完美解决URL含有中文字符串
2016/05/13 PHP
thinkPHP实现签到功能的方法
2017/03/15 PHP
关于PHP转换超过2038年日期出错的问题解决
2017/06/28 PHP
phpcms配置列表页以及获得文章发布时间
2017/07/04 PHP
PHP设计模式之原型模式定义与用法详解
2018/04/03 PHP
mongodb和php的用法详解
2019/03/25 PHP
JS关键字变色实现思路及代码
2013/02/21 Javascript
将nodejs打包工具整合到鼠标右键的方法
2013/05/11 NodeJs
jquery cookie实现的简单换肤功能适合小网站
2013/08/25 Javascript
javascript静态页面传值的三种方法分享
2013/11/12 Javascript
JS+CSS实现DIV层的展开、收缩效果
2016/01/28 Javascript
Bootstrap Paginator分页插件与ajax相结合实现动态无刷新分页效果
2016/05/27 Javascript
基于angular2 的 http服务封装的实例代码
2017/06/29 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
2017/11/27 Javascript
基于IView中on-change属性的使用详解
2018/03/15 Javascript
React Navigation 使用中遇到的问题小结
2018/05/08 Javascript
微信小程序Flex布局用法深入浅出分析
2019/04/25 Javascript
layer弹出层自适应高度,垂直水平居中的实现
2019/09/16 Javascript
vue tab切换,解决echartst图表宽度只有100px的问题
2020/07/19 Javascript
在vue中实现echarts随窗体变化
2020/07/27 Javascript
JQuery Ajax如何实现注册检测用户名
2020/09/25 jQuery
[01:38]完美世界高校联赛决赛花絮
2018/12/02 DOTA
运动检测ViBe算法python实现代码
2018/01/09 Python
django请求返回不同的类型图片json,xml,html的实例
2018/05/22 Python
Python之Django自动实现html代码(下拉框,数据选择)
2020/03/13 Python
tensorflow模型文件(ckpt)转pb文件的方法(不知道输出节点名)
2020/04/22 Python
BeautifulSoup中find和find_all的使用详解
2020/12/07 Python
python实现双人五子棋(终端版)
2020/12/30 Python
html5 css3实例教程 一款html5和css3实现的小机器人走路动画
2014/10/20 HTML / CSS
澳大利亚现代波西米亚风格女装网站:Bohemian Traders
2018/04/16 全球购物
土木工程应届生自荐信
2013/09/24 职场文书
英文版销售经理个人求职信
2013/11/20 职场文书
2015质检员个人年终工作总结
2015/10/23 职场文书
Nginx反向代理、重定向
2022/04/13 Servers