Django个人博客搭建8-优化文章模块

Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块

1.文章分页

利用Django内置的分页模块:Paginator类(:Paginator官网例子),因为是对文章分页,因此需要修改article/views.py中article_list视图:

from django.core.paginator import Paginator



# 文章列表函数
def article_list(request):
    # 取出所有博客文章
    article_list = ArticlePost.objects.all()
    # 每页显示一篇文章
    paginator = Paginator(article_list, 1)
    # 获取url中的页码
    page = request.GET.get('page')
    # 将导航对象相应的页码内容返回给articles
    articles = paginator.get_page(page)
    # 需要传递给模板对象
    context = {'articles': articles}
    # render函数载入模板,并返回context对象
    return render(request, 'article/list.html', context)
    # article/list.html:模板位置          context:传入模板的对象

接下来修改templates/article/list.html加入分页内容

...
<div class="pagination row">
    <div class="m-auto">
        <span class="step-links">
            <!-- 如果不是第一页,则显示上翻按钮 -->
            {% if articles.has_previous %}
                <a href="?page=1" class="btn btn-success">
                    &laquo; 1
                </a>
                <span>...</span>
                <a href="?page={{ articles.previous_page_number }}"
                   class="btn btn-secondary"
                >
                    {{ articles.previous_page_number }}
                </a>
            {% endif %}
            <!-- 当前页面 -->
            <span class="current btn btn-danger btn-lg">
                {{ articles.number }}
            </span>
            <!-- 如果不是最末页,则显示下翻按钮 -->
            {% if articles.has_next %}
                <a href="?page={{ articles.next_page_number }}"
                   class="btn btn-secondary"
                >
                    {{ articles.next_page_number }}
                </a>
                <span>...</span>
                <a href="?page={{ articles.paginator.num_pages }}"
                   class="btn btn-success"
                >
                    {{ articles.paginator.num_pages }} &raquo;
                </a>
            {% endif %}
        </span>
    </div>
</div>

最后查看效果:
在这里插入图片描述
在这里插入图片描述
分页功能完成

2.文章浏览量

首先修改文章的模型article/models.py

# 博客文章数据模型
class ArticlePost(models.Model):
...
    updated = models.DateTimeField(auto_now=True)
    total_views = models.PositiveIntegerField(default=0)
...

PositiveIntegerField是用于存储正整数的字段
接着生成迁移

python manage.py makemigrations
F:\Desktop\myblog>python manage.py makemigrations
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Migrations for 'article':
  article\migrations\0003_auto_20190210_1956.py
    - Add field total_views to articlepost
    - Alter field created on articlepost
Migrations for 'userprofile':
  userprofile\migrations\0002_auto_20190210_1956.py
    - Alter field avatar on profile

执行迁移:

F:\Desktop\myblog>python manage.py migrate
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Operations to perform:
  Apply all migrations: admin, article, auth, contenttypes, sessions, userprofile
Running migrations:
  Applying article.0003_auto_20190210_1956... OK
  Applying userprofile.0002_auto_20190210_1956... OK

F:\Desktop\myblog>

我们一般需要在文章列表和文章详情中显示各个文章的浏览量,因此先修改article/list.html

...
<div class="card-footer">
                        <a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">阅读本文</a>
                        <span>
                            <small class="col align-self-end" style="color: gray;">
                                浏览量:{{ article.total_views }}
                            </small>
                        </span>
                        ...

接着修改详情模板article/detail.html:

<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load staticfiles %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
    文章详情
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 文章详情 -->
    <div class="container">
       ...
                {% if user == article.author %}
            .<a href="#" onclick="confirm_delete()">删除文章</a>
            .<a href="{% url "article:article_update" article.id %}">编辑文章</a>
                {% endif %}
            </div>
            <div>
                浏览量:{{ article.total_views }}
            </div>
            <div class="col-12">
                <p>{{ article.body|safe }}</p>
            ...
{% endblock content %}

首先修改得就是检查登陆用户是否为文章作者本人,如果是本人才会显示删除和编辑文章,接着在下面增加显示浏览量
重启服务器:

在这里插入图片描述
可以看见文章列表可以显示文章浏览量了,点击阅读本文:
在这里插入图片描述
前面修改了修改删除文章得权限,因此用户没有登陆时时没有这两个选项的,我们点击登陆在这里插入图片描述
登陆后就显示了,但是由于没有对浏览量的进行逻辑处理,就是文章的浏览量总是初始给定的0,因此修改article/views.py中article_detail使其点击一次文章详情total_views就+1:

扫描二维码关注公众号,回复: 11277472 查看本文章
def article_detail(request, id):
    ...
    article.total_views += 1
    article.save(update_fields=['total_views'])
	...

update_fields=[]指定了数据库只更新total_views字段,优化执行效率。
我们重新刷新一下详情页面:
在这里插入图片描述
发现文章的浏览量已经自动增加了
这样文章浏览量功能就已经完成了

3.根据浏览量排序最热文章:

重写article/views.py:article_list():

# 文章列表函数
def article_list(request):
    # 根据GET请求中查询条件
    # 返回不同排序的对象数组
    if request.GET.get('order') == 'total_views':
        article_list = ArticlePost.objects.all().order_by('-total_views')
        order = 'total_views'
    else:
        article_list = ArticlePost.objects.all()
        order = 'normal'
    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)
    # 修改此行
    context = {'articles': articles, 'order': order}
    return render(request, 'article/list.html', context)

'-total_views’为反序,即文章浏览量高的在前面
接下来修改templates/article/list.html

...
{% block content %}
<!-- 定义放置文章标题的div容器 -->
    <div class="container">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views">
                    最热
                </a>
            </li>
        </ol>
        </nav>
        <div class="row mt-2">
            {% for article in articles %}
            <!-- 文章内容 -->
            ...

重启服务器点击最热查看效果:
在这里插入图片描述
可以看见文章排序是按照文章浏览量排序的了
这样最热文章功能就完成了

4.搜索文章功能

Q对象
Model.objects.all()能够返回表中的所有对象。

对应的,Model.objects.filter(**kwargs)可以返回与给定参数匹配的部分对象。

还有Model.objects.exclude(**kwargs)返回与给定参数不匹配的对象

如果想对多个参数进行查询怎么办?比如同时查询文章标题和正文内容。这时候就需要Q对象。
首先修改article/views.py

# 文章列表函数
def article_list(request):
    search = request.GET.get('search')
    order = request.GET.get('order')
    # 用户搜索逻辑,请求中有search,则走次逻辑
    if search:
        if order == 'total_views':
        # 用q对象进行联合搜索
            article_list = ArticlePost.objects.filter(
                Q(title__contains=search)|  # icontains不区分分大小写,contains区分大小写
                Q(body__icontains=search)
            ).order_by('-total_views')

        else:
            article_list = ArticlePost.objects.filter(
                Q(title__contains=search) |
                Q(body__icontains=search)
            )
    # 根据GET请求中查询条件
    # 返回不同排序的对象数组
    else:
        search = ''
        if order == 'total_views':
            article_list = ArticlePost.objects.all().order_by('-total_views')
        else:
            article_list = ArticlePost.objects.all()
    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)
    # 修改此行
    context = {'articles': articles, 'order': order, 'search': search}
    return render(request, 'article/list.html', context)

接着修改模板:article/list.html

 <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views">
                    最热
                </a>
            </li>
        </ol>
        </nav>
        <div class="row">
            <div class="col-auto mr-auto">
                <form class="form-inline">
                    <label class="sr-only">
                        content
                    </label>
                    <input type="text"
                           class="form-control"
                            name="search"
                            placeholder="搜索文章..."
                            required>
                </form>
            </div>
        </div>
        {# 搜索提示语 #}
        {% if search %}
            <h4>
                <span style="color: red">{{ search }}</span>
                的搜索内容如下
            </h4>
            <hr>
            {% else %}
                <h4>暂无<span style="color: red">{{ search }}</span> </h4>
                有关的文章
            <hr>
        {% endif %}
        <div class="row mt-2">
            {% for article in articles %}
            <!-- 文章内容 -->
            <div class="col-4 mb-4">
            <!-- 卡片容器 -->
                <div class="card h-100">
                    <!-- 标题 -->
                    <h4 class="card-header">{{ article.title }}</h4>
                    <!-- 摘要 -->
                    <div class="card-body">
                        <p class="card-text">{{ article.body|slice:'100' }}...</p>
                    </div>
                    <!-- 注脚 -->
                    <div class="card-footer">
                        <a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">阅读本文</a>
                        <span>
                            <small class="col align-self-end" style="color: gray;">
                                浏览量:{{ article.total_views }}
                            </small>
                        </span>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>

<div class="pagination row">
    <div class="m-auto">
        <span class="step-links">
            <!-- 如果不是第一页,则显示上翻按钮 -->
            {% if articles.has_previous %}
                <a href="?page=1&order={{ order }}&search={{ search }}" class="btn btn-success">
                    &laquo; 1
                </a>
                <span>...</span>
                <a href="?page={{ articles.previous_page_number }}&order={{ order }}&search={{ search }}"
                   class="btn btn-secondary"
                >
                    {{ articles.previous_page_number }}
                </a>
            {% endif %}
            <!-- 当前页面 -->
            <span class="current btn btn-danger btn-lg">
                {{ articles.number }}
            </span>
            <!-- 如果不是最末页,则显示下翻按钮 -->
            {% if articles.has_next %}
                <a href="?page={{ articles.next_page_number }}&order={{ order }}&search={{ search }}"
                   class="btn btn-secondary"
                >
                    {{ articles.next_page_number }}
                </a>
                <span>...</span>
                <a href="?page={{ articles.paginator.num_pages }}&order={{ order }}&search={{ search }}"
                   class="btn btn-success"
                >
                    {{ articles.paginator.num_pages }} &raquo;
                </a>
            {% endif %}
        </span>
    </div>
</div>

面包屑组件、页码组件都改动了href:增加了search参数
新增搜索栏,以GET请求提交search参数;required属性阻止用户提交空白文本
新增搜索提示语。好的UI必须让用户了解当前的状态
我们重启服务器打开文章列表页面
搜索java
在这里插入图片描述
这样文章的搜索功能就完成了

5.渲染Markdown目录

首先修改article/views.py:

# 文章详情
def article_detail(request, id):
    ...
    article.body = markdown.markdown(article.body,
                                     extensions=[
                                         # 包含 缩写、表格等常用扩展
                                         'markdown.extensions.extra',
                                         # 语法高亮扩展
                                         'markdown.extensions.codehilite',
                                         # 目录扩展
                                         'markdown.extensions.toc',
                                     ])
...
                                     

添加了一行代码,接着我们在之前的文章里添加几个级别的标题
在这里插入图片描述
其实就是插入不同级别的标题,然后在文章任何地方插入**[TOC]**即可自动生成目录
点击完成后观察效果:
在这里插入图片描述
如果我们想实现将目录插入到页面任何一个位置就徐娅萍修改Markdown的渲染方法:

# 文章详情
def article_detail(request, id):
    article = ArticlePost.objects.get(id=id)
    article.total_views += 1
    article.save(update_fields=['total_views'])
    md = markdown.Markdown(
        extensions=[
            # 包含 缩写、表格等常用扩展
            'markdown.extensions.extra',
            # 语法高亮扩展
            'markdown.extensions.codehilite',
            # 目录扩展
            'markdown.extensions.toc',
        ]
    )
    article.body = md.convert(article.body)
    context = {'article': article, 'toc': md.toc}
    # 载入模板,并返回context对象p
    return render(request, 'article/detail.html', context)

为了能将toc单独提取出来,我们先将Markdown类赋值给一个临时变量md,然后用convert()方法将正文渲染为html页面。通过md.toc将目录传递给模板。

修改article/detail.html

...
<!-- 文章详情 -->
    <div class="container">
        <div class="row">
            <div class="col-9">
                <h1 class="col-12 mt-4">{{ article.title }}</h1>
                <div class="col-12 alert alert-success">作者: {{ article.author }}
                    {% if user == article.author %}
                .<a href="#" onclick="confirm_delete()">删除文章</a>
                .<a href="{% url "article:article_update" article.id %}">编辑文章</a>
                    {% endif %}
                </div>
                <div>
                    浏览量:{{ article.total_views }}
                </div>
                <div class="col-12">
                    <p>{{ article.body|safe }}</p>
                </div>
            </div>
            {# 目录 #}
            <div class="col-3 mt-4">
                <h4>
                    <strong>目录</strong>
                    <hr>
                    <div>
                        {{ toc|safe }}
                    </div>
                </h4>
            </div>
        </div>
    </div>
    <script>
        //删除文章的函数
        function confirm_delete() {
        ...

将原来的内容装进col-9的容器中,将右侧col-3的空间留给目录,toc需要|safe标签才能正确渲染
我们重新刷新页面:
在这里插入图片描述
发现右边已经正确显示目录了

Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块

猜你喜欢

转载自blog.csdn.net/weixin_43249914/article/details/86846571