美文网首页
Django搭建个人博客:文章标签功能

Django搭建个人博客:文章标签功能

作者: 杜赛_dusai | 来源:发表于2019-02-12 21:42 被阅读0次

    “标签”是作者从文章中提取的核心词汇,其他用户可以通过标签快速了解文章的关注点。每一篇文章的标签可能都不一样,并且还可能拥有多个标签,这是与栏目功能不同的。

    好在标签功能也有优秀的三方库: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>
    ...
    

    运行服务器,就可以在发表页面看到效果了:

    image

    多个标签最好用英文逗号进行分隔。中文逗号有的版本会报错,干脆就不要去使用了。

    列表中显示标签

    虽然保存标签的功能已经实现了,还得把它显示出来才行。

    显示标签最常用的位置是在文章列表中,方便用户筛选感兴趣的文章。

    修改文章列表的模板,将标签显示出来:

    templates/article/list.html
    
    ...
    <!-- 栏目 -->
    ...
    
    <!-- 标签 -->
    <span>
        {% for tag in article.tags.all %}
            <a href="#"
               class="badge badge-secondary" 
            >
                {{ tag }}
            </a>
        {% endfor %}
    </span>
    
    ...
    

    链接中的class中是Bootstrap定义的徽章样式

    插入位置紧靠在栏目按钮的后面。当然你想放到其他位置也是完全可以的。

    刷新列表页面看看效果:

    image

    标签过滤

    有时候用户想搜索带有某一个标签的所有文章,现在就来做这个功能。

    与搜索功能一样,只需要调取数据时用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字段中过滤nametag的数据条目。赋值的字符串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">
        &laquo; 1
    </a>
    
    <!-- 上面3条是举例,其他类似地方也请补充进去 -->
    ...
    

    标签过滤功能就完成了。

    Django-taggit更多的用法请阅读官方文档:Django-taggit

    总结

    本章学习了使用Django-taggit来完成标签功能。

    在学习阶段,你可以不借助他人的轮子,自己实现功能:瞎折腾对掌握基础有很大帮助。

    实际开发时,又分为两种情况:

    • 浅层需求某项通用功能,开发完成后改动不大:此类功能建议尽量使用轮子,加快开发效率。人生苦短,能节约的时间,一秒钟都不要浪费。
    • 需要大量定制化的功能,开发完成后需要频繁改动:此类功能因为经常对底层代码进行改动,与其在别人的代码上修修补补,还不如自己从头写了。自己的代码不仅熟悉,而且都是为定制化而生的。

    到底如何选择,就根据你的喜欢进行斟酌了。


    相关文章

      网友评论

          本文标题:Django搭建个人博客:文章标签功能

          本文链接:https://www.haomeiwen.com/subject/qcdleqtx.html