用django搭建自己的博客(三)-發(fā)郵件(轉(zhuǎn))

使用高級(jí)特性來(lái)優(yōu)化你的博客

在上一章中,你創(chuàng)建了一個(gè)基礎(chǔ)的博客應(yīng)用。現(xiàn)在你將要改造它成為一個(gè)功能更加齊全的博客即供,利用一些高級(jí)的特性例如通過(guò)email來(lái)分享帖子,添加評(píng)論于微,給帖子打上tag逗嫡,檢索出相似的帖子。在本章中株依,你將會(huì)學(xué)習(xí)以下幾點(diǎn):

  • 使用Django發(fā)送email
  • 在views中創(chuàng)建并操作表單
  • 通過(guò)models創(chuàng)建表單
  • 構(gòu)建復(fù)雜的QuerySets

通過(guò)email分享帖子

首先驱证,我們會(huì)允許用戶(hù)通過(guò)發(fā)送郵件來(lái)分享他們的帖子。首先讓我們花費(fèi)一小會(huì)時(shí)間來(lái)想下你該如何使用views恋腕,urls和templates來(lái)創(chuàng)建這個(gè)功能根據(jù)你在上一章中學(xué)到的知識(shí)∧ǔ現(xiàn)在,核對(duì)一下你需要哪幾點(diǎn)才能允許你的用戶(hù)通過(guò)郵箱來(lái)發(fā)送帖子荠藤。你需要做到以下幾點(diǎn):

  • 創(chuàng)建一個(gè)表單給用戶(hù)來(lái)填寫(xiě)他們的姓名伙单,email,收件方以及評(píng)論哈肖,評(píng)論不是必選的车份。
  • 在views.py文件中創(chuàng)建一個(gè)view來(lái)操作發(fā)布的數(shù)據(jù)和發(fā)送email
  • 在博客應(yīng)用的urls.py中為新的view添加一個(gè)URL pattern
  • 創(chuàng)建一個(gè)模板來(lái)展示這個(gè)表單

使用Django創(chuàng)建表單

讓我們開(kāi)始創(chuàng)建一個(gè)用來(lái)分享帖子的表單。Django有一個(gè)內(nèi)置的表單框架允許你通過(guò)簡(jiǎn)單的方式來(lái)創(chuàng)建表單牡彻。這個(gè)表單框架允許你定義你的表單字段扫沼,指定他們必須展示的方式,以及指定他們?nèi)绾悟?yàn)證輸入的數(shù)據(jù)庄吼。Django表單框架還提供一個(gè)靈活的方式來(lái)渲染表單以及操作數(shù)據(jù)缎除。
Django應(yīng)用了兩個(gè)基礎(chǔ)類(lèi)來(lái)創(chuàng)建表單:

  • Form: 允許你創(chuàng)建一個(gè)標(biāo)準(zhǔn)表單
  • ModelForm: 允許你創(chuàng)建一個(gè)表單可用于創(chuàng)建或者更新model的實(shí)例

首先,創(chuàng)建一個(gè)forms.py文件在你博客應(yīng)用的目錄下总寻,輸入以下代碼:

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)

這是你的第一個(gè)Django表單器罐。看下代碼:我們已經(jīng)創(chuàng)建了一個(gè)繼承基礎(chǔ)Form類(lèi)的表單渐行。我們使用不同的字段類(lèi)型以使Django來(lái)驗(yàn)證字段轰坊。

表單可以存在你的Django項(xiàng)目的任何地方铸董,但按照慣例將它們放在每一個(gè)應(yīng)用下面的forms.py文件中

name字段是一個(gè)CharField。這種類(lèi)型的字段等同于<input type=“text”>HTML元素肴沫。每個(gè)字段都有默認(rèn)的控件來(lái)確定它在HTML中的展示粟害。通過(guò)改變控件的屬性可以重寫(xiě)默認(rèn)的控件。在comment字段中颤芬,我們使用Textarea控件來(lái)使它展示成一個(gè)<textarea></textarea>HTML元素來(lái)代替默認(rèn)的<input>元素悲幅。

字段的驗(yàn)證也依賴(lài)于字段的類(lèi)型。舉個(gè)例子站蝠,email和to字段是EmailField,它們需要一個(gè)有效的地址汰具,否則字段驗(yàn)證不通過(guò)將會(huì)返回forms.ValidationError異常導(dǎo)致表單提交失敗。其他的參數(shù)將進(jìn)入表單驗(yàn)證:我們指定name字段最多只能輸入25個(gè)字符菱魔,通過(guò)設(shè)置required=False表明comments字段不是必填項(xiàng)留荔。目前我們?cè)诒韱沃惺褂玫倪@些字段類(lèi)型只是Django支持的表單字段的一部分。要查看更多可利用的表單字段澜倦,你可以訪問(wèn):https://docs.djangoproject.com/en/1.8/ref/forms/fields/

現(xiàn)在聚蝶,你需要學(xué)習(xí)如何通過(guò)Django來(lái)發(fā)送email。

使用Django發(fā)送email

通過(guò)Django發(fā)送email非常簡(jiǎn)單肥隆。首先既荚,你需要有一個(gè)本地的SMTP服務(wù)或者獲取到一個(gè)外部SMTP服務(wù)的配置稚失,接下來(lái)在你的項(xiàng)目中的setting.py文件中設(shè)置如下內(nèi)容:

EMAIL_HOST: SMTP服務(wù)地址栋艳。默認(rèn)本地。
EMAIL_POSR: SMATP服務(wù)端口句各,默認(rèn)25吸占。
EMAIL_HOST_USER: SMTP服務(wù)的用戶(hù)名。
EMAIL_HOST_PASSWORD: SMTP服務(wù)的密碼凿宾。
EMAIL_USE_TLS: 是否使用TLS加密協(xié)議矾屯。
EMAIL_USE_SSL: 是否使用SSL加密協(xié)議。

如果你沒(méi)有本地SMTP服務(wù)初厚,你可以使用你的email提供的SMTP服務(wù)件蚕。下面提供了一個(gè)簡(jiǎn)單的例子展示如何通過(guò)使用Google賬戶(hù)的Gmail服務(wù)來(lái)發(fā)送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服務(wù)基本都是上面那樣設(shè)置,不過(guò)我的沒(méi)搞好产禾,一直報(bào)錯(cuò)排作,于是自己寫(xiě)了個(gè)腳本 send_mail.py

#coding:utf8

import smtplib
from email.mime.text import MIMEText

# 發(fā)送郵件函數(shù)
class SendMail():

    def __init__(self):
        # 設(shè)置服務(wù)器名稱(chēng)、用戶(hù)名亚情、密碼以及郵件后綴
        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中操作表單

你必須創(chuàng)建一個(gè)新的view當(dāng)表單成功提交后進(jìn)行操作和發(fā)送email妄痪。編輯博客應(yīng)用下的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視圖楞件,參數(shù)為request對(duì)象和post_id衫生。
  • 我們使用get_object_or_404方法通過(guò)ID獲取對(duì)應(yīng)的帖子裳瘪,并且確保取回的帖子一定是已發(fā)布的狀態(tài)。
  • 我們使用同一個(gè)view來(lái)展示初始表單以及處理提交后的數(shù)據(jù)罪针。
  • 我們會(huì)區(qū)別被提交的表單和不基于這次請(qǐng)求方法的表單彭羹。我們通過(guò)使用POST來(lái)提交表單。我們假設(shè)我們會(huì)遇到GET請(qǐng)求站故,這時(shí)就需要展示一個(gè)空的表單皆怕,而我們遇到POST請(qǐng)求,我們就需要處理表單提交上來(lái)的數(shù)據(jù)西篓。因此愈腾,我們使用request.method == 'POST'
    來(lái)區(qū)分這兩種場(chǎng)景。

下面是展示和操作表單的進(jìn)程:

  1. 當(dāng)view加載后遇到一個(gè)GET請(qǐng)求岂津,我們會(huì)創(chuàng)建一個(gè)新的表單實(shí)例在模板中展示一個(gè)空的表單:
form = EmailPostForm()
  1. 當(dāng)用戶(hù)填寫(xiě)了表單并通過(guò)POST方式提交虱黄,我們會(huì) 創(chuàng)建一個(gè)表單實(shí)例來(lái)使用提交的數(shù)據(jù),這些數(shù)據(jù)被包含在request.POST中:
if request.method == 'POST':
    # Form was submitted
    form = EmailPostForm(request.POST)
  1. 在以上步驟之后吮成,我們通過(guò)表單的is_valid()方法來(lái)驗(yàn)證提交的數(shù)據(jù)橱乱。這個(gè)方法會(huì)驗(yàn)證表單引進(jìn)的數(shù)據(jù),如果所有的字段都是有效數(shù)據(jù)粱甫,將會(huì)返回True泳叠。一旦有任何一個(gè)字段包含無(wú)效的數(shù)據(jù),is_valid()將會(huì)返回False茶宵。你可以通過(guò)訪問(wèn)form.errors
    查看所有驗(yàn)證錯(cuò)誤的列表危纫。
  1. 如果表單數(shù)據(jù)驗(yàn)證沒(méi)有通過(guò),我們會(huì)再次使用提交的數(shù)據(jù)在模板中渲染表單乌庶。我們會(huì)在模板中顯示驗(yàn)證錯(cuò)誤提示种蝶。
  2. 如果表單數(shù)據(jù)驗(yàn)證通過(guò),我們通過(guò)訪問(wèn)form.cleaned_data

# 獲取驗(yàn)證過(guò)的數(shù)據(jù)瞒大。這個(gè)屬性是一個(gè)表單字段和值的字典螃征。

如果表單中的數(shù)據(jù)沒(méi)有通過(guò)驗(yàn)證,cleaned_data只會(huì)包含驗(yàn)證過(guò)的數(shù)據(jù)

請(qǐng)注意透敌,我們聲明了一個(gè)sent變量并且賦予它True當(dāng)帖子被成功發(fā)送盯滚。當(dāng)表單成功提交的時(shí)候,我們會(huì)使用這個(gè)變量用來(lái)在template中顯示一條成功提示酗电。因?yàn)槲覀冃枰趀mail中包含帖子的超鏈接魄藕,所以我們通過(guò)使用post.get_absolute_url()方法來(lái)取到帖子的絕對(duì)路徑。我們將這個(gè)絕對(duì)路徑作為request.build_absolute_uri()的輸入值來(lái)構(gòu)建一個(gè)完整的HTTP鏈接顾瞻。我們通過(guò)使用驗(yàn)證過(guò)的表單數(shù)據(jù)來(lái)構(gòu)建email的主題和消息內(nèi)容并最終發(fā)送email給表單中的to字段中包含的所有email地址泼疑。

現(xiàn)在你的view已經(jīng)完成了,別忘記為它去添加一個(gè)新的URL pattern。打開(kāi)你的博客應(yīng)用下的urls.py文件添加post_share的URL pattern如下所示:

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

在templates中渲染表單

在通過(guò)創(chuàng)建表單退渗,編寫(xiě)view以及添加URL patter后移稳,我們就只剩下為這個(gè)view添加tempalte了。在blog/templates/blog/post/
目錄下創(chuàng)建一個(gè)新的文件并命名為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 %}

這個(gè)tempalte專(zhuān)門(mén)用來(lái)顯示一個(gè)表單或email成功發(fā)送后展示一條成功提示信息个粱。就像你所看見(jiàn)的,我們創(chuàng)建的HTML表單元素里面聲明了提交方式采用的是POST方法翻翩;
{% csrf_token %}tempalte標(biāo)簽引進(jìn)了一個(gè)隱藏的字段包含一個(gè)自動(dòng)生成的標(biāo)記來(lái)避開(kāi)Cross-Site request forgery(CSRF)攻擊都许。這些攻擊由一個(gè)惡意的站點(diǎn)或程序組成并執(zhí)行一個(gè)不需要的操作給你站點(diǎn)中的用戶(hù)。你可以找到更多的信息通過(guò)訪問(wèn)https://en.wikipedia.org/wiki/Cross-site_request_forgery 嫂冻。上述的標(biāo)簽生成的隱藏字段就像下面一樣:

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

默認(rèn)情況下胶征,Django在所有的POST請(qǐng)求中都會(huì)檢查CSRF標(biāo)簽。請(qǐng)記住要在所有使用POST方法提交的表單中包含csrf_token標(biāo)簽桨仿。(譯者注:當(dāng)然你也可以關(guān)閉這個(gè)檢查睛低,注釋掉app_list中的csrf應(yīng)用即可)

現(xiàn)在訪問(wèn)試試:
點(diǎn)擊一篇帖子,點(diǎn)擊Share this post你會(huì)看到一個(gè)包含通過(guò)email分享這個(gè)帖子的表單服傍∏祝看上去如下所示:

share_to_mail

為這個(gè)表單提供的CSS樣式被包含在示例代碼中的 static/css/blog.css文件中。當(dāng)你點(diǎn)擊Send e-mail按鈕吹零,這個(gè)表單會(huì)提交并驗(yàn)證罩抗。如果所有的字段都通過(guò)了驗(yàn)證,你會(huì)得到一條成功信息如下所示:

send_succed

如果你輸入了錯(cuò)誤的數(shù)據(jù)灿椅,你會(huì)看到表單被再次渲染套蒂,并展示出驗(yàn)證錯(cuò)誤信息;
檢查郵箱會(huì)看到剛剛發(fā)送的郵件

mail
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末阱扬,一起剝皮案震驚了整個(gè)濱河市泣懊,隨后出現(xiàn)的幾起案子伸辟,更是在濱河造成了極大的恐慌麻惶,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件信夫,死亡現(xiàn)場(chǎng)離奇詭異窃蹋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)静稻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)警没,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人振湾,你說(shuō)我怎么就攤上這事杀迹。” “怎么了押搪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵树酪,是天一觀的道長(zhǎng)浅碾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)续语,這世上最難降的妖魔是什么垂谢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮疮茄,結(jié)果婚禮上滥朱,老公的妹妹穿的比我還像新娘。我一直安慰自己力试,他們只是感情好徙邻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著畸裳,像睡著了一般鹃栽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躯畴,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天民鼓,我揣著相機(jī)與錄音,去河邊找鬼蓬抄。 笑死丰嘉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嚷缭。 我是一名探鬼主播饮亏,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阅爽!你這毒婦竟也來(lái)了路幸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤付翁,失蹤者是張志新(化名)和其女友劉穎简肴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體百侧,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砰识,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了佣渴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辫狼。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辛润,靈堂內(nèi)的尸體忽然破棺而出膨处,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布真椿,位于F島的核電站秦叛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瀑粥。R本人自食惡果不足惜挣跋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狞换。 院中可真熱鬧避咆,春花似錦、人聲如沸修噪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)黄琼。三九已至樊销,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脏款,已是汗流浹背围苫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撤师,地道東北人剂府。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像剃盾,于是被迫代替她去往敵國(guó)和親腺占。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容