“標籤”是作者從文章中提取的核心詞彙,其他用戶可以通過標籤快速瞭解文章的關注點。每一篇文章的標籤可能都不一樣,並且還可能擁有多個標籤,這是與欄目功能不同的。
好在標籤功能也有優秀的三方庫:Django-taggit,省得自己動手設計了。快速開發就是這樣,能“借用”就不要自己重複勞動。
安裝及設置
首先在虛擬環境中安裝Django-taggit:
pip install django-taggit
安裝成功後,修改項目設置以添加庫:
my_blog/settings.py
...
INSTALLED_APPS = [
...
'taggit',
]
...
修改文章模型
標籤是文章Model的屬性,因此需要修改文章模型。
需要注意的是標籤引用的不是內置字段,而是庫中的TaggableManager
,它是處理多對多關係的管理器:
article/models.py
...
# Django-taggit
from taggit.managers import TaggableManager
...
class ArticlePost(models.Model):
...
# 文章標籤
tags = TaggableManager(blank=True)
...
然後記得數據遷移。
帶標籤文章的發表
修改文章的表單類,讓其能夠提交標籤字段:
article/forms.py
...
class ArticlePostForm(forms.ModelForm):
class Meta:
...
fields = ('title', 'body', 'tags')
然後修改發表文章的視圖,保存POST中的標籤:
article/views.py
...
def article_create(request):
# 已有代碼
if request.method == "POST":
article_post_form = ArticlePostForm(data=request.POST)
if article_post_form.is_valid():
new_article = article_post_form.save(commit=False)
...
new_article.save()
# 新增代碼,保存 tags 的多對多關係
article_post_form.save_m2m()
...
需要注意的是,如果提交的表單使用了commit=False
選項,則必須調用save_m2m()
才能正確的保存標籤,就像普通的多對多關係一樣。
最後就是在發表文章的模板中添加標籤的表單項了:
templates/article/create.html
...
<!-- 提交文章的表單 -->
<form method="post" action=".">
...
<!-- 文章標籤 -->
<div class="form-group">
<label for="tags">標籤</label>
<input type="text"
class="form-control col-3"
id="tags"
name="tags"
>
</div>
...
</form>
...
運行服務器,就可以在發表頁面看到效果了:
多個標籤最好用英文逗號進行分隔。中文逗號有的版本會報錯,乾脆就不要去使用了。
列表中顯示標籤
雖然保存標籤的功能已經實現了,還得把它顯示出來才行。
顯示標籤最常用的位置是在文章列表中,方便用戶篩選感興趣的文章。
修改文章列表的模板,將標籤顯示出來:
templates/article/list.html
...
<!-- 欄目 -->
...
<!-- 標籤 -->
<span>
{% for tag in article.tags.all %}
<a href="#"
class="badge badge-secondary"
>
{{ tag }}
</a>
{% endfor %}
</span>
...
鏈接中的class
中是Bootstrap定義的徽章樣式。
插入位置緊靠在欄目按鈕的後面。當然你想放到其他位置也是完全可以的。
刷新列表頁面看看效果:
標籤過濾
有時候用戶想搜索帶有某一個標籤的所有文章,現在就來做這個功能。
與搜索功能一樣,只需要調取數據時用filter()
方法過濾結果就可以了。
修改<a>
標籤中的href
,使其帶有tag
參數返回到View中:
templates/article/list.html
...
<!-- 標籤 -->
<span>
{% for tag in article.tags.all %}
<a href="{% url 'article:article_list' %}?tag={{ tag }}"
class="badge badge-secondary"
>
{{ tag }}
</a>
{% endfor %}
</span>
...
然後在View中取得tag
的值,並進行搜索。
下面的代碼將article_list()
函數完整寫出來了(包括上一章末尾沒講的欄目查詢),方便讀者比對。
article/views.py
...
def article_list(request):
# 從 url 中提取查詢參數
search = request.GET.get('search')
order = request.GET.get('order')
column = request.GET.get('column')
tag = request.GET.get('tag')
# 初始化查詢集
article_list = ArticlePost.objects.all()
# 搜索查詢集
if search:
article_list = article_list.filter(
Q(title__icontains=search) |
Q(body__icontains=search)
)
else:
search = ''
# 欄目查詢集
if column is not None and column.isdigit():
article_list = article_list.filter(column=column)
# 標籤查詢集
if tag and tag != 'None':
article_list = article_list.filter(tags__name__in=[tag])
# 查詢集排序
if order == 'total_views':
article_list = article_list.order_by('-total_views')
paginator = Paginator(article_list, 3)
page = request.GET.get('page')
articles = paginator.get_page(page)
# 需要傳遞給模板(templates)的對象
context = {
'articles': articles,
'order': order,
'search': search,
'column': column,
'tag': tag,
}
return render(request, 'article/list.html', context)
...
注意Django-taggit中標籤過濾的寫法:filter(tags__name__in=[tag])
,意思是在tags
字段中過濾name
爲tag
的數據條目。賦值的字符串tag
用方括號包起來。
之所以這樣寫是因爲Django-taggit還支持多標籤的聯合查詢,比如:
Model.objects.filter(tags__name__in=["tag1", "tag2"])
爲了實現帶參數的交叉查詢,還要將翻頁等位置的href
修改一下:
templates/article/list.html
...
<!-- 所有類似地方加上 tag 參數,如排序、翻頁等 -->
<a href="{% url 'article:article_list' %}?search={{ search }}&column={{ column }}&tag={{ tag }}">
最新
</a>
...
<a href="{% url 'article:article_list' %}?order=total_views&search={{ search }}&column={{ column }}&tag={{ tag }}">
最熱
</a>
...
<a href="?page=1&order={{ order }}&search={{ search }}&column={{ column }}&tag={{ tag }}" class="btn btn-success">
« 1
</a>
<!-- 上面3條是舉例,其他類似地方也請補充進去 -->
...
標籤過濾功能就完成了。
Django-taggit更多的用法請閱讀官方文檔:Django-taggit
總結
本章學習了使用Django-taggit來完成標籤功能。
在學習階段,你可以不借助他人的輪子,自己實現功能:瞎折騰對掌握基礎有很大幫助。
實際開發時,又分爲兩種情況:
- 淺層需求某項通用功能,開發完成後改動不大:此類功能建議儘量使用輪子,加快開發效率。人生苦短,能節約的時間,一秒鐘都不要浪費。
- 需要大量定製化的功能,開發完成後需要頻繁改動:此類功能因爲經常對底層代碼進行改動,與其在別人的代碼上修修補補,還不如自己從頭寫了。自己的代碼不僅熟悉,而且都是爲定製化而生的。
到底如何選擇,就根據你的喜歡進行斟酌了。
- 有疑問請在杜賽的個人網站留言,我會盡快回復。
- 或Email私信我:[email protected]
- 項目完整代碼:Django_blog_tutorial