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">
« 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 }} »
</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:
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">
« 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 }} »
</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-增加文章評論模塊