通过邮箱分享贴子
首先,让我们允许用户通过电子邮件来分享帖子。请花一点时间思考,如何使用在上一节学到的 views.py
, URLs
和 templates
去创建这个功能。为了能允许你的用户使用邮箱分享帖子,你需要做下面的这些事情:
- 创建一个表单,供用户填写它们的名字,电子邮件,邮件收件人,以及可选的评论。
- 在
views.py
文件中创建一个视图,它用于处理 post 数据,并且发送邮件。 - 在blog应用程序的urls.py文件中为新视图添加一个URL模式
- 创建一个模板来显示表单
1. 使用 Django 创建表单
让我们开始创建表单去分享帖子。 Django 有一个内置的 表单 矿建,它允许你使用简单的方式创建表单,定制它如何显示以及验证它输入的数据。Django 表单框架提供了一个灵活的方式去渲染表单和处理数据。
Django 附带了两个基本类去创建表单:
-
Form
: 允许你创建标准的表单。 -
ModelForm
: 允许你构建绑定到模型实例的表单。
首先,在你的 blog
应用程序的目录中创建一个 forms.py
文件,并且使它看起来如下所示:
from django import forms
class EmailPostForm(forms.Form):
name = forms.CharField(max_length=25)
email = forms.EmailField()
to = forms.EmailField()
comments = forms.CharField(required=False,
widget=forms.Textarea)
这是你的第一个 Django 表单。来看一下这个代码,你通过继承 Form
基础类创建了一个表单。Django 使用不同的字段类型去验证相应的字段。
表单可以存在你Django 项目的任何地方。惯例是将它们放在每个应用程序的 forms.py 文件中。
name
字段是 CharField
。 这个类型的字段被渲染为一个 <input type="text">
HTML 节点。每一个字段类型有一个默认的部件,它决定了如何把这个字段渲染到 HTML 中。 通过 widget
属性去覆盖默认的部件。在comments
字段中,使用了一个 Textarea
部件去让它作为 <textarea>
HTML 节点代替默认的 <input>
节点显示。
字段验证还取决于字段的类型。例如,email
和 to
字段 就是 EmailField
字段。这两个字段都必须是一个可发的 email 地址,否则将抛出一个 forms.ValidationError
异常,并且让这个表单无效。表单验证也会去考虑其他的参数: 你在 name
字段上定义了一个最大长度 (maximum
) 为 25 个字符。并且用 required=False
让 comments
字段变为可选,所有这些都要考虑到字段验证。在这个表单中使用的字段类型仅仅是Django 表单字段的一部分。所有的字段验证在这个字段中,看这个链接。
2. 在视图中处理表单
你需要创建一个视图,在数据成功提交的时候,去处理表单数据并且发送一个邮件。编辑你的 blog
应用程序中的 views.py
文件,添加以下代码:
from .forms import EmailPostForm
def post_share(request, post_id):
# 通过 id 检索帖子
post = get_object_or_404(Post, id=post_id, status = 'published')
if request.method == 'POST':
# 表单被提交
form = EmailPostForm(request.POST)
if form.is_valid():
# 表单字段通过验证
cd = form.cleaned_data
# ....发送邮件
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html',
{'post': post, 'form': form})
视图工作原理如下:
- 定义了一个以
request
对象和post_id
变量作为参数的一个post_share
视图。 - 使用
get_object_or_404()
快捷方式通过 ID 检索帖子,并且确保 检索到的帖子具有published
状态。 - 可以使用相同的视图来显示初始表单和处理提交的数据。你可以根据
request
方法区分是否提交了表单,并使用POST
提交数据。假设收到一个GET
请求,就显示一个空表单,如果收到了一个POST
请求,则提交该表单并且需要处理。因此,你需要使用request.method == 'POST'
去区分这两种情况。
表单的显示和处理流程如下:
- 在最初使用GET请求加载视图时, 你创建了一个 新的表单实例,它被用来在模板种显示一个新的表单。
form = EmailPostForm()
- 用户填写完表单,并且通过
POST
进行提交。这是,你使用包含在request.POST
中提交的数据,创建了一个表单实例。
if request.method == 'POST':
# 表单被提交
form = EmailPostForm(request.POST)
- 之后,你使用
is_valid()
方法验证提交的数据。此方法验证表单中引入的数据,如果所有字段都包含有效数据,则返回True
。如果任何一个字段包含无效数据,这时is_valid()
返回False
。你可以通过访问form.errors
查看验证错误列表。 - 如果表单不是有效的,则使用提交的数据在模板中再次呈现表单。你将显示验证错误到模板中。
- 如果表单是有效的,通过访问
form.cleaned_data
来检索经过验证的数据。这个属性是包含表单字段和它们值得字段。
如果表单数据没有通过验证,
cleaned_data
将仅仅包含有效字段。
现在,让我们探索如何使用Django发送邮件来将所有内容组合在一起。
3. 使用Django 发送邮件
使用 Django 发送邮件时很简单的。首先,你需要有一个本地的 SMTP(Simple mail Transfer Protocol) 服务器,或者通过在项目的 settings.py
文件中添加以下设置来定义外部 SMTP 服务器的配置:
-
EMAIL_HOST
: SMTP 服务器地址,默认为 localhost。 -
EMAIL_PORT
: SMTP 服务器端口,默认为 25。 -
EMAIL_HOST_USER
: SMTP 服务器的用户名。 -
EMAIL_HOST_PASSWORD
: SMTP 服务器的密码。 -
EMAIL_USE_TLS
: 是否使用一个 Transport Layer Security (TLS) 安全连接。 -
EMAIL_USE_SSL
: 是否使用一个 隐式的 TLS 安全连接。
如果你不能使用 SMTP 服务器,你可以通过在 setting.py
文件中添加以下设置来告诉 Django 向控制台写入电子邮件:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
通过使用这个配置,Django 将把所有的 emails 输出到 shell 中,这在你的应用中没有 SMTP 服务器测试时是很有用的。
如果你想要发送邮件,但是你没有本地 SMTP 服务器,你可以使用 email 服务提供者提供的 SMTP 服务。以下实例配置使用一个 Google 账号 通过 Gmail 服务器发送邮件是有效的。
EMAIL_HOST = ' smtp.gmail.com'
EMAIL_HOST_USER = ' your_account@gmail.com'
EMAIL_HOST_PASSWORD = ' your_password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
打开 python shell 运行 python manage.py shell
,并且发送邮件,如下:
(译者注: 这里是使用的 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
)
(译者注: 这里我是配置了一个 163 的邮箱服务器,对比可以看到,两者还是有一点点区别的。)
send_mail()
函数接受 subject
, message
, sender
, 和 recipients
列表作为所需的参数。通过设置 可选参数fail_silently = False
, 告诉它如果邮件没有发送正常, 就抛出一个异常。如果你看到输出 1, 这时邮件就是发送成功了。
如果你是用前面的配置使用 Gmail 发送邮件,你将不得不启用访问不太安全的应用程序,https://myaccount.google.com/lesssecureapps, 如下:
(译者注: 这里我没有 Google 账号 具体操作一遍。其实用国内的QQ邮箱,163邮箱,可能方便一点)
在这个例子中,你使用 Django 发送邮件可能还要禁用 Gamil 的验证码。
编辑在 blog
应用程序里面 views.py
中的 post_share
视图,如下所示:
from django.core.mail import send_mail
def post_share(request, post_id):
# 通过 id 检索帖子
post = get_object_or_404(Post, id=post_id, status = 'published')
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 = f"{cd['name']} recommends you read {post.title}"
message = f"Read {post.title} at {post_url} \n\n {cd['name']}\'s comments: {cd['comments']}"
send_mail(subject, message, 'gznbgznb@163.com', [cd['to']])
sent = True
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html',
{'post': post, 'form': form, 'sent': sent})
如果你使用 SMTP 邮件服务器,而不是 控制台的 EmailBackend
,就需要使用 你自己真实的 邮箱账号替换 admin@myblog.com
。
上面的代码声明了一个 sent
变量,当 帖子发送出去了以后 就把它设置为 True
。 你可以在之后当表单成功提交的时候把成功的消息显示在模板中。
因为你必须在邮件中包含一个链接,你使用一个 get_absolute_url()
方法获得 帖子的 绝对路径,你使用这个路径作为 requests.build_absolute_uri()
的输入去构建一个完整的 URL,其中包含了 HTTP 模式和 主机名。你使用 通过验证了以后并且处理的数据创建邮件的 主题 和消息主题。最后把邮件发送给 表单中的 to
字段发送给邮件地址。
现在你的视图就已经完成了,记住要为它添加一个 新的 路由模式,打开你的blog
应用程序的 urls.py
文件,并且添加一个 post_share
路由模式,如下:
urlpatterns = [
# ....
path('<int:post_id>/share/', views.post_share, name='post_share'),
]
4. 在模板中渲染表单
创建表单之后,编写视图,添加url模式,你就仅仅只缺视图的模板了。在 blog/templates/blog/post
目录中创建一个新文件,并且取名叫 share.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 {{ form.cleaned_data.to }}.
</p>
{% else %}
<h1>Share "{{ post.title }}" by e-mail</h1>
<form method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
</form>
{% endif %}
{% endblock %}
这个模板显示表单,或者当邮件发送成功后显示消息。你们会注意到,你创建了 HTML 表单节点,指明了它不得不以 POST
方式进行提交:
<form method="post">
然后在包含实际的表单实例中,你可以告诉Django使用 as_p
方法去字段渲染为HTML 的 <p>
段节点。你也可以通过表单的 as_ul
渲染为无序列表,或者是 as_table
渲染为 HTML 的表格。如果你向去渲染每一个字段,你可以迭代这些字段,{{ form.as_p }}
就像下面的例子:
{% for field in form %}
<div>
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
这个{% csrf_token %}
模板标签引入了一个隐藏字段,用来自动生成token 去避免 CSRF(cross-site request forgery) 攻击。这个攻击来自恶意的网站,或者是为站点上的用户执行不需要的操作的程序。你可以在这个链接中发现更多有关的信息。
前面的那个标签生成了一个隐藏字段就像下面这样:
<input type='hidden' name='csrfmiddlewaretoken' value='26Jj Ko2lcEtYkGo V9z4XmJIEHLXN5LDR' />
默认情况下,Django 会从所有的 POST 请求中检查这个
CSRF token
。 请记住,在所有通过POST
提交的表单中都要包括csrf_token
标签。
编辑 blog/post/detail.html
模板,并且在 {{ post.body | linebreaks }}
变量后面添加 分享帖子的URL。
<p>
<a href="{% url "blog:post_share" post.id %}">
Share this post
</a>
</p>
请记住,您正在使用Django提供的{% url %}
模板标记动态地构建URL。你将使用名为 名为 blog
的命名空间和名为 post_share
的 URL,并且通过 帖子 ID 作为参数构建一个 绝对URL。
现在,使用 python manage.py runserver
启动开发者服务器,并且在你的浏览器中 打开 http://127.0.0.1:8000/blog/
。点击任意一个帖子的标题去查看详情页面。在帖子主题下面,你将看到你刚刚添加的链接,如下截图所示:
点击 Share this post, 你应该可以看到这个页面,包含通过 emil 分享帖子的表单,如下所示:
表单的 css 样式包含在示例代码的 static/css/blog.css
文件中。当你点击 **SEND E-MAIL 按钮是,这个表单被提交并且进行检验,如果你的所有表单字段都是合法数据,你就会得到一个成功的消息,如下所示:
如果你输入了无效的数据,表单会被重新渲染,并且包含无效的错误信息,如下:
image.png
注意,现代的一些浏览器会阻止你提交带有空的或者错误的文本的表单。这是因为浏览器根据表单字段类型和每个字段的限制进行表单验证。这个例子中,表单没有提交,并且浏览器先为错误的字段显示了错误信息。
通过邮箱分享帖子已经完成。现在让我们来为你的 blog 创建一个 评论系统。
网友评论