django项目实例精解(3)利用高级特性完善博客程序

第二章内容:
利用django发送邮件
创建表单并在视图中加以处理
从模型中创建表单
整合第三方应用程序
构建复杂的QuerySet

2.1通过电子邮件共享帖子

针对帖子的邮件发送功能,需要执行以下操作:

  1. 创建用户表单,并填写名字,邮件,收件人,可选的备注功能。
  2. 在views.py文件中创建视图,并处理数据以及发送邮件.
  3. 在blog应用程序的urls.py文件中,针对新视图添加url路径。
  4. ’ 创建模板并显示表单。

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)
    

setting中配置邮件参数:

# 发送邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# smpt服务地址
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 25   # 端口默认都是25不需要修改
# 发送邮件的邮箱,需要配置开通SMTP
EMAIL_HOST_USER = '[email protected]'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'xxx'
# 收件人看到的发件人
EMAIL_FROM = '带签名的<[email protected]>'

views.py中处理表单:

def post_share(request, post_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 = '{} ({}) 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})

配置blog/urls.py:

path('<int:post_id>/share/', views.post_share, name='post_share'),

在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 action="." method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
</form>
{% endif %}
{% endblock %}

然后运行程序就可以发送邮件分享了,注意setting和view中的发送邮箱[email protected]要一致。
在这里插入图片描述
感觉做的有点累,但是加油,我们才刚开始。。。继续努力。

2.2构建评论系统

第一步创建模型,blog中models.py文件中写代码:

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE,
                             related_name='comments')
    name = models.CharField(max_length=80)
    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)

运行命令行进行迁移

python manage.py makemigrations blog
python manage.py migrate

在blog/admin.py中导入comment模型:

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'post', 'created', 'active')
    list_filter = ('active', 'created', 'updated')
    search_fields = ('name', 'email', 'body')

创建模型中的表单,这里使用ModelForm:

from blog.models import Comment
class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

修改blog/views.py中post_detail函数:

def post_detail(request, year, month, day, post):  # 获取独立的帖子
    post = get_object_or_404(Post, slug=post,
                             status='published',
                             publish__year=year,
                             publish__month=month,
                             publish__day=day)
    comments = post.comments.filter(active=True)
    new_comment = None
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.post = post
            new_comment.save()
    else:
        comment_form = CommentForm()
    return render(request, 
                  'blog/post/detail.html',
                  {"post": post,
                    'comments': comments,
                    'new_comment': new_comment,
                    'comment_form': comment_form})

修改post/detail.html

  1. 显示全部评论数量
  2. 显示评论列表
  3. 向用户显示一个表单,并可添加新的评论内容。
{% extends "blog/base.html" %}
{% block title %}{{ post.title }} {% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
    Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
<p>
    <a href="{% url 'blog:post_share' post.id %}">
    Share this post</a>
</p>
{% with comments.count as total_comments %}
<h2>
    {{ total_comments}} comment{{ total_comments|pluralize }}
</h2>
{% endwith %}
{% for comment in comments %}
<div class="comment">
    <p class="info">
        Comment{{ forloop.counter }} by {{ comment.name }}
        {{ comment.created }}
    </p>
    {{ comment.body|linebreaks}}
</div>
{% empty %}
<p>There are no comments yet.</p>
{% endfor %}
{% if new_comment %}
<h2>Your comment has been added.</h2>
{% else %}
<h2>Add a new comment</h2>
<form action="." method="post">
    {{ comment_form.as_p }}
    {% csrf_token %}
    <p><input type="submit" value="Add comment"></p>
</form>
{% endif %}

{% endblock %}

访问网址,可以看到下面内容,提交评论测试成功。
在这里插入图片描述

2.3 添加标签功能

第三方插件django-taggit模块,书上是0.22.2版本,我安装的是1.2.0版本。

pip install django_taggit==0.22.2

setting.py中注册

INSTALLED_APPS = [
    ...
    'taggit',
]

在models.py中的Post函数中实例化。

class Post(models.Model):  # 继承models.Model
    ...
    tags = TaggableManager()  # 添加标签

然后数据库迁移两步操作。

python manage.py makemigrations blog
python manage.py migrate

运行项目,打开http://127.0.0.1:8000/admin/taggit/tag/
添加标签。给每个文章添加标签。
在blog/post/list.html中帖子标题h2下加入p标签,使用逗号join:

<p class="tags">Tags:{{ post.tags.all|join:"," }}</p>

修改blog/views.py,这里又使用了函数视图,完整代码如下:

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)  # 每页显示三个
    page = request.GET.get("page")
    try:
        posts = paginator.page(page)
    except PageNotAnInteger:  # 不是整数,返回第一个页面
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'blog/post/list.html',
                  {'page': page, 'posts': posts, 'tag': tag})

注意修改url,我把此时的blog/urls.py整个贴出来:

from django.urls import path
from . import views
urlpatterns = [
    path('', views.post_list, name='post_list'),
    # path('', views.PostListView.as_view(), name='post_list'),
    path('<int:year>/<int:month>/<int:day>/<slug:post>/',
         views.post_detail,
         name='post_detail'),
    path('<int:post_id>/share/', views.post_share, name='post_share'),
    path('tag/<slug:tag_slug>/', views.post_list, name='post_list_by_tag'),
]

这里的两个路径指向了同一个视图,但采用了不同的name,第一种不含参数,第二个使用tag_slug参数,使用slug路径转化器。

修改blog/post/list.html,我把全部代码贴出来:

{% extends "blog/base.html" %}
{% block title %}{% endblock %}
{% block content %}
<h1>My blog</h1>

{% if tag %}
<h2>Posts tagged with "{{ tag.name }}"</h2>
{% endif %}

{% for post in posts %}
<h2>
    <a href="{{ post.get_absolute_url }}">
        {{ post.title }}
    </a>
</h2>
<!--修改标签的显示方式-->
<p class="tags">Tags:
    <!--{{ post.tags.all|join:"," }}-->
    {% for tag in post.tags.all %}
    <a href="{% url 'blog:post_list_by_tag' tag.slug %}">
        {{ tag.name }}
    </a>
    {% if not forloop.last %}, {% endif %}
    {% endfor %}
</p>
<p>
    Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
<!--{% include "pagination.html" with page=page_obj %}-->
{% endblock %}

访问链接,测试查看,需要提前在admin中给文章添加标签。
http://127.0.0.1:8000/blog/
点击tag访问。

2.4根据相似性检索帖子。

通过共有的标签号显示相似的帖子,通过这个方式,当用户访问某个帖子时,可向其推荐阅读相关帖子。
步骤:

  1. 针对当前帖子检索全部标签,
  2. 获取包含特定标签的全部帖子,
  3. 从结果列表中排除当前帖子,以避免推荐相同帖子,
  4. 通过当前帖子的标签号,对结果进行排序,
  5. 如果具有相同标签号的两个或者多个帖子,推荐使用最近发布的帖子,
  6. 将查询限制为希望推荐的帖子数量
    视图函数中增加:
from taggit.models import Tag
def post_detail(request, year, month, day, post):
	...
	post_tags_ids = Post.tags.values_list('id', flat=True)
    similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
    similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-publish')[:4]

    return render(request,
                  'blog/post/detail.html',
                  {...
                   'similar_posts': similar_posts})

blog/post/detail.html中在评论前面增加:

<!--增加相似推荐-->
<h2>Similar posts</h2>
{% for post in similar_posts %}
<p>
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</p>
{% empty %}
There are no similar posts yet.
{% endfor %}

在这里插入图片描述
第二章完,继续努力。

发布了48 篇原创文章 · 获赞 0 · 访问量 733

猜你喜欢

转载自blog.csdn.net/qq_36710311/article/details/104755144