2.1 通过邮箱分享帖子

2020-07-20  本文已影响0人  gznb

通过邮箱分享贴子

首先,让我们允许用户通过电子邮件来分享帖子。请花一点时间思考,如何使用在上一节学到的 views.py, URLstemplates 去创建这个功能。为了能允许你的用户使用邮箱分享帖子,你需要做下面的这些事情:

1. 使用 Django 创建表单

让我们开始创建表单去分享帖子。 Django 有一个内置的 表单 矿建,它允许你使用简单的方式创建表单,定制它如何显示以及验证它输入的数据。Django 表单框架提供了一个灵活的方式去渲染表单和处理数据。

Django 附带了两个基本类去创建表单:

首先,在你的 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>节点显示。

字段验证还取决于字段的类型。例如,emailto 字段 就是 EmailField 字段。这两个字段都必须是一个可发的 email 地址,否则将抛出一个 forms.ValidationError 异常,并且让这个表单无效。表单验证也会去考虑其他的参数: 你在 name 字段上定义了一个最大长度 (maximum) 为 25 个字符。并且用 required=Falsecomments 字段变为可选,所有这些都要考虑到字段验证。在这个表单中使用的字段类型仅仅是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})

视图工作原理如下:

表单的显示和处理流程如下:

  1. 在最初使用GET请求加载视图时, 你创建了一个 新的表单实例,它被用来在模板种显示一个新的表单。
    form = EmailPostForm()
  2. 用户填写完表单,并且通过 POST 进行提交。这是,你使用包含在 request.POST中提交的数据,创建了一个表单实例。
if request.method == 'POST':
    # 表单被提交
  form = EmailPostForm(request.POST)
  1. 之后,你使用 is_valid() 方法验证提交的数据。此方法验证表单中引入的数据,如果所有字段都包含有效数据,则返回 True。如果任何一个字段包含无效数据,这时 is_valid() 返回 False。你可以通过访问 form.errors 查看验证错误列表。
  2. 如果表单不是有效的,则使用提交的数据在模板中再次呈现表单。你将显示验证错误到模板中。
  3. 如果表单是有效的,通过访问form.cleaned_data来检索经过验证的数据。这个属性是包含表单字段和它们值得字段。

如果表单数据没有通过验证,cleaned_data 将仅仅包含有效字段。

现在,让我们探索如何使用Django发送邮件来将所有内容组合在一起。

3. 使用Django 发送邮件

使用 Django 发送邮件时很简单的。首先,你需要有一个本地的 SMTP(Simple mail Transfer Protocol) 服务器,或者通过在项目的 settings.py 文件中添加以下设置来定义外部 SMTP 服务器的配置:

如果你不能使用 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,并且发送邮件,如下:

image.png

(译者注: 这里是使用的 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend')

image.png

(译者注: 这里我是配置了一个 163 的邮箱服务器,对比可以看到,两者还是有一点点区别的。)

send_mail() 函数接受 subject, message, sender, 和 recipients 列表作为所需的参数。通过设置 可选参数fail_silently = False, 告诉它如果邮件没有发送正常, 就抛出一个异常。如果你看到输出 1, 这时邮件就是发送成功了。

如果你是用前面的配置使用 Gmail 发送邮件,你将不得不启用访问不太安全的应用程序,https://myaccount.google.com/lesssecureapps, 如下:

image.png
(译者注: 这里我没有 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/。点击任意一个帖子的标题去查看详情页面。在帖子主题下面,你将看到你刚刚添加的链接,如下截图所示:

image.png

点击 Share this post, 你应该可以看到这个页面,包含通过 emil 分享帖子的表单,如下所示:

image.png

表单的 css 样式包含在示例代码的 static/css/blog.css 文件中。当你点击 **SEND E-MAIL 按钮是,这个表单被提交并且进行检验,如果你的所有表单字段都是合法数据,你就会得到一个成功的消息,如下所示:

image.png

如果你输入了无效的数据,表单会被重新渲染,并且包含无效的错误信息,如下:


image.png

注意,现代的一些浏览器会阻止你提交带有空的或者错误的文本的表单。这是因为浏览器根据表单字段类型和每个字段的限制进行表单验证。这个例子中,表单没有提交,并且浏览器先为错误的字段显示了错误信息。

通过邮箱分享帖子已经完成。现在让我们来为你的 blog 创建一个 评论系统。

上一篇下一篇

猜你喜欢

热点阅读