通过email分享帖子
1.给用户创建一个表单来填写他们的姓名,email,收件人以及评论,评论不是必选项。
2.在views.py文件中创建一个视图(view)来操作发布的数据和发送email
3.在blog应用的urls.py中为新的视图(view)添加一个URL模式
4.创建一个模板(template)来展示这个表单
1.创建表单
在应用目录下创建forms.py文件
更多表单字段选项查看https://docs.djangoproject.com/en/1.8/ref/forms/fields/
from django import forms # 从django中导入forms
class EmailPostForm(forms.Form): # 继承标准表单类Form
name = forms.CharField(max_length=25) # name字段是一个CharField。这种类型的字段被渲染成
email = forms.EmailField() #
to = forms.EmailField()
comments = forms.CharField(required=False,
widget=forms.Textarea) # widget控件我们使用HTML元素,默认为,\
# required为是否必须,默认为True
2.在视图中操作表单
from .forms import EmailPostForm
def post_share(request, post_id):
post = get_object_or_404(Post, id=post_id, status='published') # 通过id与status获取到帖子对象
cd = None
if request.method == 'POST': # 如果请求方式为POST
form = EmailPostForm(request.POST) # 当用户填写好了表单并通过POST提交表单。之后,我们会用保存在request.POST中提交的数据创建一个表单实例
if form.is_valid(): # 我们使用表单的is_valid()方法来验证提交的数据。这个方法会验证表单引进的数据,如果所有的字段都是有效数据,将会返回True。一旦有任何一个字段是无效的数据,is_valid()就会返回False。你可以通过访问 form.errors来查看所有验证错误的列表。
cd = form.cleaned_data # 如果表单数据验证通过,我们通过访问form.cleaned_data获取验证过的数据。这个属性是一个表单字段和值的字典
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html', {'post': post, 'form': form, 'cd': cd})
使用django发送邮件
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com' #SMTP地址 例如: smtp.163.com
EMAIL_PORT = 25 #SMTP端口 例如: 25
EMAIL_HOST_USER = '' #qq的邮箱 例如: [email protected]
EMAIL_HOST_PASSWORD = '' #我的邮箱密码 例如 xxxxxxxxx
EMAIL_SUBJECT_PREFIX = u'django' #为邮件Subject-line前缀,默认是'[django]'
EMAIL_USE_TLS = True #与SMTP服务器通信时,是否启动TLS链接(安全链接)。默认是false
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
网站的SMTP端口号为25
运行命令python manage.py shell来打开Python shell,发送一封email如下所示:
>>> from django.core.mail import send_mail
>>> send_mail('Django mail', 'This e-mail was sent with Django.','[email protected]', ['[email protected]'], fail_silently=False)
send_mail()方法需要这些参数:邮件主题,内容,发送人以及一个收件人的列表。通过设置可选参数fail_silently=False,我们告诉这个方法如果email没有发送成功那么需要抛出一个异常。如果你看到输出是1,证明你的email发送成功了。
完整视图
from django.core.mail import send_mail
def post_share(request, post_id):
post = get_object_or_404(Post, id=post_id, status='published') # get得到id=post_id,status='published'的对象
sent = False # 默认sent
if request.method == 'POST': # 如果请求方式为POST
form = EmailPostForm(request.POST) # 用保存在request.POST的内容创建表单实例
if form.is_valid(): # 如果验证通过
cd = form.cleaned_data # 验证的数据的字典
post_url = request.build_absolute_uri(post.get_absolute_url())# 通过使用post.get_absolute_url()方法来获取到帖子的绝对路径。我们将这个绝对路径作为request.build_absolute_uri()的输入值来构建一个完整的包含了HTTP schema和主机名的url
subject = '{} ({}) recommends you reading "{}"'.format(cd['name'], cd['email'], post.title)
message = 'Read "{}" at {}\n\n{}\'s comments: {}'.format(post.title, post_url, cd['name'], cd['comments'])
send_mail(subject, message, '[email protected]', [cd['to']])
sent = True
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html', {'post': post, 'form': form, 'sent': sent})
3.添加URL模式
urlpatterns = [
# ...
url(r'^(?P\d+)/share/$', views.post_share,
name='post_share'),
]
4.创建一个模板
{% extends "blog/base.html" %}
{% block title %}Share a post{% endblock %}
{% block content %}
{% if sent %}
E-mail successfully sent
"{{ post.title }}" was successfully sent to {{ cd.to }}.
{% else %}
Share "{{ post.title }}" by e-mail
{% endif %}
{% endblock %}
编辑你的blog/post/detail.html模板(template),在{{ post.body|linebreaks }}变量后面添加如下的链接来分享帖子的URL:
通过使用Django提供的{% url %}模板(template)标签(tag)来动态的生成URL。我们以blog为命名空间,以post_share为URL,同时传递帖子ID作为参数来构建绝对的URL。
创建评论模型
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
name = models.CharField(max_length=200)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ("created",)
def __str__(self):
return "comment by {} on {}".format(self.name, self.post)
以上就是我们的Comment模型(model)。它包含了一个外键将一个单独的帖子和评论关联起来。在Comment模型(model)中定义多对一(many-to-one)的关系是因为每一条评论只能在一个帖子下生成,而每一个帖子又可能包含多个评论。related_name属性允许我们给这个属性命名,这样我们就可以利用这个关系从相关联的对象反向定位到这个对象。定义好这个之后,我们通过使用comment.post就可以从一条评论来取到对应的帖子,以及通过使用post.comments.all()来取回一个帖子所有的评论。如果你没有定义related_name属性,Django会使用这个模型(model)的名称加上_set(在这里是:comment_set)来命名从相关联的对象反向定位到这个对象的manager。
https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_one/可以学习更多关于多对一的关系。
注册评论模型
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'post', 'created', 'active') # 在管理站点中的列表显示
list_filter = ('active', 'created', 'updated') # 过滤显示
search_fields = ('name', 'email', 'post', 'body') # 搜索显示
admin.site.register(Comment, CommentAdmin)
通过模型models创建表单
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body',)
在视图(views)中操作ModelForms
def post_detail(request, year, month, day, post): # year,month,day,post等参数是从urls文件中获取
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day) # 与Post.objects.get()方法相似,get_object_or_404获取不到对象会返回404
comments = post.comments.filter(active=True) # 当前post帖子的所有评论(comments)
new_comment = None
if request.method == "POST": # 如果是POST请求
comment_form = CommentForm(data=request.POST) # 把requests.POST中的数据创建表单实例
if comment_form.is_valid(): # 如果数据经过难
new_comment = comment_form.save(
commit=False) # 创建表单连接的实例,如果你调用这个方法时设置comment=False,你创建的模型(model)实例不会即时保存到数据库中。当你想在最终保存之前修改这个model对象会非常方便,我们接下来将做这一步骤。save()方法是给ModelForm用的,而不是给Form实例用的,因为Form实例没有关联上任何模型(model)。
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
return render(request, 'blog/post/detail.html',
{'post': post, 'comments': comments, 'comment_form': comment_form, 'new_comment': new_comment})
帖子详情面中添加评论
{% with comments.count as total_comments %}
{{ total_comments }} comment{{ total_comments|pluralize }}
{% endwith %}
在模板(template)中我们使用Django ORM执行comments.count() 查询集(QuerySet)。注意,Django模板(template)语言中不使用圆括号来调用方法。{% with %} 标签(tag)允许我们分配一个值给新的变量,这个变量可以一直使用直到遇到{% endwith %}标签(tag)。
{% with %}模板(template)标签(tag)是非常有用的,可以避免直接操作数据库或避免多次调用花费较多的方法。
根据total_comments的值,我们使用pluralize 模板(template)过滤器(filter)为单词comment显示复数后缀。模板(Template)过滤器(filters)获取到他们输入的变量值,返回计算后的值。我们将会在第三章 扩展你的博客应用中讨论更多的模板过滤器(tempalte filters)。
pluralize模板(template)过滤器(filter)在值不为1时,会在值的末尾显示一个"s"。之前的文本将会被渲染成类似:0 comments, 1 comment 或者 N comments。Django内置大量的模板(template)标签(tags)和过滤器(filters)来帮助你以你想要的方式来显示信息。
{% for comment in comments %}
There are no comments yet.
{% endfor %}
我们使用{% for %}模板(template)标签(tag)来循环所有的评论。如果comments列为空我们会显示一个默认的信息,告诉我们的用户这篇帖子还没有任何评论。我们使用 {{ forloop.counter }}变量来枚举所有的评论,在每次迭代中该变量都包含循环计数。之后我们显示发送评论的用户名,日期,和评论的内容。
{% if new_comment %}
Your comment has been added.
{% else %}
Add a new comment
增加标签(tagging)功能
安装第三方标签应用:pip3 install django-taggit
在settings中设置第三方应用taggit
INSTALLED_APPS = (
# ...
'blog',
'taggit',
)
打开你的blog应用下的model.py文件,给Post模型(model)添加django-taggit提供的TaggableManager管理器(manager)
from taggit.managers import TaggableManager
class Post(models.Model):
# ...
tags = TaggableManager()
写入数据库
python3 manage.py migrations
python3manage.py migrate
现在已经可以使用django-taggit模型(model)。打开终端运行命令python manage.py shell来学习如何使用tags管理器(manager)。首先,我们取回我们的其中一篇帖子(该帖子的ID为1)
>>> from blog.models import Post
>>> post = Post.objects.get(id=1)
添加标签
>>> post.tags.add('music', 'jazz', 'django')
>>> post.tags.all()
[, , ]
移除标签
>>> post.tags.remove('django')
>>> post.tags.all()
[, ]
现在,我们准备编辑我们的blog帖子来显示这些标签。打开blog/post/list.html 模板(template)在帖子标题下方添加如下HTML代码:
编辑视图文件:
def post_list(request, tag_slug=None):
object_list = Post.published.all()
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
object_list = object_list.filter(tags__in=[tag])
paginator = Paginator(object_list, 3) # 3 posts in each page
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
posts = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
posts = paginator.page(paginator.num_pages)
return render(request, 'blog/post/list.html', {'page': page,
'posts': posts,
'tag': tag})
打开blog应用下的url.py文件,注释基于类的PostListView URL模式,然后取消post_list视图(view)的注释,如下所示:
url(r'^$', views.post_list, name='post_list'),
# url(r'^$', views.PostListView.as_view(), name='post_list'),
添加下面额外的URL pattern到通过标签过滤过的帖子列表中:
url(r'^tag/(?P[-\w]+)/$',views.post_list,
name='post_list_by_tag'),
因为我们要使用post_list视图(view),编辑blog/post/list.html 模板(template),使用posts对象修改pagination,如下所示:
#{% include "pagination.html" with page=page_obj %}
{% include "pagination.html" with page=posts %}
现在,我们循环一个帖子的所有标签,通过某一标签来显示一个自定义的链接URL。我们通过{% url "blog:post_list_by_tag" tag.slug %},用URL的名称以及标签 slug作为参数来构建URL。我们使用逗号分隔这些标签。
标签应用可以自己使用manytomany来构建
检索类似的帖子
为了通过一个特定的帖子检索到类似的帖子,我们需要做到以下几点:
- 返回当前帖子的所有标签。
- 返回所有带有这些标签的帖子。
- 在返回的帖子列表中排除当前的帖子,避免推荐相同的帖子。
- 通过和当前帖子共享的标签数量来排序所有的返回结果。
- 假设有两个或多个帖子拥有相同数量的标签,推荐最近的帖子。
- 限制我们想要推荐的帖子数量。
post_tags_ids = post.tags.values_list('id',
flat=True) # 取出tag对象的id的值,values返回是字典列表queryset;values_list返回的是元组列表queryset对象,values_list加上flat=True返回值的列表queryset对象,也可以使用post.tags.all()返回queryset,循环后可得到models,
similar_posts = Post.published.filter(tags__in=post_tags_ids) \
.exclude(id=post.id) # 取出所有的相同tag的id值的对象,exclude排除当前帖子
similar_posts = similar_posts.annotate(same_tags=Count('tags')) \
.order_by('-same_tags', '-publish')[:4]
#annotate语句,为queryset(similar_posts)中的每一个models.POST对象添加same_tags字段,此字段Count了每一个post对象的"tags"对象的个数,然后返回queryset
django-taggit还内置了一个similar_objects()
管理器(manager)使你可以通过共享的标签返回所有对象。你可以通过访问 http://django-taggit.readthedocs.org/en/latest/api.html 看到所有django-taggit管理器。
学习来源于夜夜月翻译的django by example
Comment {{ forloop.counter }} by {{ comment.name }} {{ comment.created }}
{{ comment.body|linebreaks }}