用django搭建自己的博客(三)-发邮件(转)

使用高级特性来优化你的博客

在上一章中,你创建了一个基础的博客应用。现在你将要改造它成为一个功能更加齐全的博客,利用一些高级的特性例如通过email来分享帖子,添加评论,给帖子打上tag,检索出相似的帖子。在本章中,你将会学习以下几点:

  • 使用Django发送email
  • 在views中创建并操作表单
  • 通过models创建表单
  • 构建复杂的QuerySets

通过email分享帖子

首先,我们会允许用户通过发送邮件来分享他们的帖子。首先让我们花费一小会时间来想下你该如何使用views,urls和templates来创建这个功能根据你在上一章中学到的知识。现在,核对一下你需要哪几点才能允许你的用户通过邮箱来发送帖子。你需要做到以下几点:

  • 创建一个表单给用户来填写他们的姓名,email,收件方以及评论,评论不是必选的。
  • 在views.py文件中创建一个view来操作发布的数据和发送email
  • 在博客应用的urls.py中为新的view添加一个URL pattern
  • 创建一个模板来展示这个表单

使用Django创建表单

让我们开始创建一个用来分享帖子的表单。Django有一个内置的表单框架允许你通过简单的方式来创建表单。这个表单框架允许你定义你的表单字段,指定他们必须展示的方式,以及指定他们如何验证输入的数据。Django表单框架还提供一个灵活的方式来渲染表单以及操作数据。
Django应用了两个基础类来创建表单:

  • Form: 允许你创建一个标准表单
  • ModelForm: 允许你创建一个表单可用于创建或者更新model的实例

首先,创建一个forms.py文件在你博客应用的目录下,输入以下代码:

from django import forms

class EmailPostForm(forms.Form):
    name = forms.CharField(max_length=25)
    to = forms.EmailField()
    comments = forms.CharField(required=False, widget=forms.Textarea)

这是你的第一个Django表单。看下代码:我们已经创建了一个继承基础Form类的表单。我们使用不同的字段类型以使Django来验证字段。

表单可以存在你的Django项目的任何地方,但按照惯例将它们放在每一个应用下面的forms.py文件中

name字段是一个CharField。这种类型的字段等同于<input type=“text”>HTML元素。每个字段都有默认的控件来确定它在HTML中的展示。通过改变控件的属性可以重写默认的控件。在comment字段中,我们使用Textarea控件来使它展示成一个<textarea></textarea>HTML元素来代替默认的<input>元素。

字段的验证也依赖于字段的类型。举个例子,email和to字段是EmailField,它们需要一个有效的地址,否则字段验证不通过将会返回forms.ValidationError异常导致表单提交失败。其他的参数将进入表单验证:我们指定name字段最多只能输入25个字符,通过设置required=False表明comments字段不是必填项。目前我们在表单中使用的这些字段类型只是Django支持的表单字段的一部分。要查看更多可利用的表单字段,你可以访问:https://docs.djangoproject.com/en/1.8/ref/forms/fields/

现在,你需要学习如何通过Django来发送email。

使用Django发送email

通过Django发送email非常简单。首先,你需要有一个本地的SMTP服务或者获取到一个外部SMTP服务的配置,接下来在你的项目中的setting.py文件中设置如下内容:

EMAIL_HOST: SMTP服务地址。默认本地。
EMAIL_POSR: SMATP服务端口,默认25。
EMAIL_HOST_USER: SMTP服务的用户名。
EMAIL_HOST_PASSWORD: SMTP服务的密码。
EMAIL_USE_TLS: 是否使用TLS加密协议。
EMAIL_USE_SSL: 是否使用SSL加密协议。

如果你没有本地SMTP服务,你可以使用你的email提供的SMTP服务。下面提供了一个简单的例子展示如何通过使用Google账户的Gmail服务来发送email:

EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'your_account@gmail.com'
EMAIL_HOST_PASSWORD = 'your_password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

用django自带的mail服务基本都是上面那样设置,不过我的没搞好,一直报错,于是自己写了个脚本 send_mail.py

#coding:utf8

import smtplib
from email.mime.text import MIMEText

# 发送邮件函数
class SendMail():

    def __init__(self):
        # 设置服务器名称、用户名、密码以及邮件后缀
        self.mail_host = "smtp.126.com"
        self.mail_user = "iphone3000@126.com"
        self.mail_pass = "xxxxxxxxxxx"
        self.mail_postfix = "126.com"

    def send_mail(self, to_list, sub, context):
        me = self.mail_user + "<"+self.mail_user+"@"+self.mail_postfix+">"
        msg = MIMEText(context,_charset="utf-8")
        msg['Subject'] = sub
        msg['From'] = me
        msg['To'] = "".join(to_list)
        try:
             send_smtp = smtplib.SMTP()
             send_smtp.connect(self.mail_host)
             send_smtp.login(self.mail_user, self.mail_pass)
             send_smtp.sendmail(me, to_list, msg.as_string())
             send_smtp.close()
             return True
        except Exception as e:
            print(e)
            return False

在views中操作表单

你必须创建一个新的view当表单成功提交后进行操作和发送email。编辑博客应用下的views.py文件,添加以下代码:

...
from .send_mail import SendMail
...
def post_share(request, post_id):
    post = get_object_or_404(Post, id=post_id, status='published')
    sendmail = SendMail()
    sent = False
    if request.method == 'POST':
        form = EmailPostForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            post_url = request.build_absolute_uri(
                post.get_absolute_url())
            subject = '{} recommends you reading "{}"'.format(cd['name'], post.title)
            message = 'Read "{}" at {}\n\n comments: {}'.format(post.title, post_url, cd['comments'])
            sendmail.send_mail([cd['to']],subject,message)
        sent = True
    else:
        form = EmailPostForm()
    return render(request, 'blog/post/share.html', {'post': post,'form': form,'sent': sent})

该view完成了以下工作:

  • 我们定义了post_share视图,参数为request对象和post_id。
  • 我们使用get_object_or_404方法通过ID获取对应的帖子,并且确保取回的帖子一定是已发布的状态。
  • 我们使用同一个view来展示初始表单以及处理提交后的数据。
  • 我们会区别被提交的表单和不基于这次请求方法的表单。我们通过使用POST来提交表单。我们假设我们会遇到GET请求,这时就需要展示一个空的表单,而我们遇到POST请求,我们就需要处理表单提交上来的数据。因此,我们使用request.method == 'POST'
    来区分这两种场景。

下面是展示和操作表单的进程:

  1. 当view加载后遇到一个GET请求,我们会创建一个新的表单实例在模板中展示一个空的表单:
form = EmailPostForm()
  1. 当用户填写了表单并通过POST方式提交,我们会 创建一个表单实例来使用提交的数据,这些数据被包含在request.POST中:
if request.method == 'POST':
    # Form was submitted
    form = EmailPostForm(request.POST)
  1. 在以上步骤之后,我们通过表单的is_valid()方法来验证提交的数据。这个方法会验证表单引进的数据,如果所有的字段都是有效数据,将会返回True。一旦有任何一个字段包含无效的数据,is_valid()将会返回False。你可以通过访问form.errors
    查看所有验证错误的列表。
  1. 如果表单数据验证没有通过,我们会再次使用提交的数据在模板中渲染表单。我们会在模板中显示验证错误提示。
  2. 如果表单数据验证通过,我们通过访问form.cleaned_data

# 获取验证过的数据。这个属性是一个表单字段和值的字典。

如果表单中的数据没有通过验证,cleaned_data只会包含验证过的数据

请注意,我们声明了一个sent变量并且赋予它True当帖子被成功发送。当表单成功提交的时候,我们会使用这个变量用来在template中显示一条成功提示。因为我们需要在email中包含帖子的超链接,所以我们通过使用post.get_absolute_url()方法来取到帖子的绝对路径。我们将这个绝对路径作为request.build_absolute_uri()的输入值来构建一个完整的HTTP链接。我们通过使用验证过的表单数据来构建email的主题和消息内容并最终发送email给表单中的to字段中包含的所有email地址。

现在你的view已经完成了,别忘记为它去添加一个新的URL pattern。打开你的博客应用下的urls.py文件添加post_share的URL pattern如下所示:

urlpatterns = [
# ...
url(r'^(?P<post_id>\d+)/share/$', views.post_share, name='post_share'),
]

在templates中渲染表单

在通过创建表单,编写view以及添加URL patter后,我们就只剩下为这个view添加tempalte了。在blog/templates/blog/post/
目录下创建一个新的文件并命名为share.html。在html文件中添加如下代码:

{% extends "blog/base.html" %}
{% block title %}Share a post{% endblock %}
{% block content %}
{% if sent %}
<h1>E-mail successfully sent</h1>
<p>
"{{ post.title }}" was successfully sent to {{ cd.to }}.
</p>
{% else %}
<h1>Share "{{ post.title }}" by e-mail</h1>
<form action="." method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
</form>
{% endif %}
{% endblock %}

这个tempalte专门用来显示一个表单或email成功发送后展示一条成功提示信息。就像你所看见的,我们创建的HTML表单元素里面声明了提交方式采用的是POST方法;
{% csrf_token %}tempalte标签引进了一个隐藏的字段包含一个自动生成的标记来避开Cross-Site request forgery(CSRF)攻击。这些攻击由一个恶意的站点或程序组成并执行一个不需要的操作给你站点中的用户。你可以找到更多的信息通过访问https://en.wikipedia.org/wiki/Cross-site_request_forgery 。上述的标签生成的隐藏字段就像下面一样:

input type='hidden' name='csrfmiddlewaretoken' value='26JjKo2lcEtYkGoV9z4XmJIEHLXN5LDR' />

默认情况下,Django在所有的POST请求中都会检查CSRF标签。请记住要在所有使用POST方法提交的表单中包含csrf_token标签。(译者注:当然你也可以关闭这个检查,注释掉app_list中的csrf应用即可)

现在访问试试:
点击一篇帖子,点击Share this post你会看到一个包含通过email分享这个帖子的表单。看上去如下所示:

share_to_mail

为这个表单提供的CSS样式被包含在示例代码中的 static/css/blog.css文件中。当你点击Send e-mail按钮,这个表单会提交并验证。如果所有的字段都通过了验证,你会得到一条成功信息如下所示:

send_succed

如果你输入了错误的数据,你会看到表单被再次渲染,并展示出验证错误信息;
检查邮箱会看到刚刚发送的邮件

mail
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容