二十、Django2.1 搭建多用户的博客网站——文章标签

0.807字数 1166阅读 528

目录:Django 2.1 从零开始搭建博客网站系列

服务器环境搭建(选学)

小试牛刀——简单的博客网站

庖丁解牛——多用户的博客网站之用户模块

庖丁解牛——多用户的博客网站之文章模块

华丽转身——多用户的博客网站之扩展功能

项目源码下载:https://github.com/jt1024/lehehe

正文:

本文的“文章标签”不同于上篇的“模板标签”,这里的文章标签就是文章作者可以为自己的文章设置标签,也就是文章的关键词。

有一个名称为 django-taggit 的应用可以直接管理文章标签,官方网站是 https://django-taggit.readthedocs.io/en/latest/index.html

1、添加文章标签

1.1 文章标签的数据模型类

在./article/models.py中新增数据模型

class ArticleTag(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="tag")
    tag = models.CharField(max_length=500)

    def __str__(self):
        return self.tag

以上代码要写在 ArticlePost 之前,并在 ArticlePost 中新增一行代码:

article_tag = models.ManyToManyField(ArticleTag, related_name='article_tag', blank=True)

执行以下代码更新数据库

python manage.py make migrations
python manage.py migrate

1.2 文章标签的表单类

在./article/forms.py中新增表单

from .models import ArticleTag

class ArticleTagForm(forms.ModelForm):
    class Meta:
        model = ArticleTag
        fields = ('tag',)

1.3 视图函数

在./article/views.py中新增article_tag函数

@login_required(login_url='/account/login')
@csrf_exempt
def article_tag(request):
    if request.method == "GET":
        article_tags = ArticleTag.objects.filter(author=request.user)
        article_tag_form = ArticleTagForm()
        return render(request, "article/tag/tag_list.html", {"article_tags": article_tags, "article_tag_form": article_tag_form})

    if request.method == "POST":
        tag_post_form = ArticleTagForm(data=request.POST)
        if tag_post_form.is_valid():
            try:
                new_tag = tag_post_form.save(commit=False)
                new_tag.author = request.user
                new_tag.save()
                return HttpResponse("1")
            except:
                return HttpResponse("the data cannot be save.")
        else:
            return HttpResponse("sorry, the form is not valid.")

1.4 设置路由

在./article/urls.py中新增路由

path('article-tag/', views.article_tag, name="article_tag"),

1.5 文章标签的模板文件

编辑 ./templates/article/leftsider.html,在有关文章的操作中增加如下一项

<p><a href="{% url 'article:article_tag'%}">文章标签</a></p>

创建 ./templates/article/tag/tag_list.html

{% extends "article/base.html" %}
{% load staticfiles %}
{% block title %}articles tag{% endblock %}
{% block content %}
<div>
    <p>添加文章标签</p>
    <form class="form-horizontal" action="." method="post">{% csrf_token %}
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>文章标签</span></div>
            <div class="col-md-10 text-left">{{article_tag_form.tag}}</div>
        </div>
        <div class="row">
            <input type="button" class="btn btn-primary btn-lg" style="margin-left:200px; margin-top:10px;" value="添加" onclick="add_tag()">
        </div>
    </form>
</div>
<div>
    <p>已有标签列表</p>
    <table class="table table-hover">
        <tr>
            <td>序号</td>
            <td>文章标签</td>
            <td>操作</td>
        </tr>
        {% for article_tag in article_tags %}
        <tr id={{ forloop.counter }}>
            <td>{{ forloop.counter }}</td>
            <td>{{ article_tag.tag }}</a></td>
            <td>--</td>
        </tr>
        {% empty %}
        <p>现在还没有标签,请添加</p>
        {% endfor %}
    </table>
</div>

<script type="text/javascript" src='{% static "js/jquery.js" %}'></script>
<script type="text/javascript" src="{% static 'js/layer.js'%}"></script>
<script type="text/javascript">

function add_tag(){
    tag = $("#id_tag").val(); 
    $.ajax({
        url: '{% url "article:article_tag" %}',
        type: "POST", 
        data: {"tag":tag}, 
        success: function(e){
            if(e=="1"){
                layer.msg("You have added a new tag."); 
                window.location.reload();
            } else { 
                layer.msg(e)
            } 
        }
    }); 
}

</script>
{% endblock %}

1.6 测试

访问 http://127.0.0.1:8000/article/article-tag

增加文章标签

2、删除文章标签

2.1 视图函数

在./article/views.py中新增del_article_tag函数

@login_required(login_url='/account/login') 
@require_POST
@csrf_exempt
def del_article_tag(request):
    tag_id = request.POST['tag_id'] 
    try:
        tag = ArticleTag.objects.get(id=tag_id) 
        tag.delete()
        return HttpResponse("1")
    except:
        return HttpResponse("2")

2.2 设置路由

在./article/urls.py中新增路由

path('del-article-tag/', views.del_article_tag, name="del_article_tag"),

2.3 模板文件

修改 ./templates/article/tag/tag_list.html

{% extends "article/base.html" %}

{% load staticfiles %}

{% block title %}articles tag{% endblock %}

{% block content %}
<div>
    <p>添加文章标签</p>
    <form class="form-horizontal" action="." method="post">{% csrf_token %}
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>文章标签</span></div>
            <div class="col-md-10 text-left">{{article_tag_form.tag}}</div>
        </div>
        <div class="row">
            <input type="button" class="btn btn-primary btn-lg" style="margin-left:200px; margin-top:10px;" value="添加" onclick="add_tag()">
        </div>
    </form>
</div>
<div>
    <p>已有标签列表</p>
    <table class="table table-hover">
        <tr>
            <td>序号</td>
            <td>文章标签</td>
            <td>操作</td>
        </tr>
        {% for article_tag in article_tags %}
        <tr id={{ forloop.counter }}>
            <td>{{ forloop.counter }}</td>
            <td>{{ article_tag.tag }}</a></td>
            <td>
                <a nane="delete" href="javascript:" onclick="del_tag(this, {{ article_tag.id }})">
                    <span class="glyphicon glyphicon-trash"></span>
                </a>
            </td>
        </tr>
        {% empty %}
        <p>You have no article tags. Please add them.</p>
        {% endfor %}
    </table>
</div>

<script type="text/javascript" src='{% static "js/jquery.js" %}'></script>
<script type="text/javascript" src="{% static 'js/layer.js'%}"></script>
<script type="text/javascript">

function add_tag(){
    tag = $("#id_tag").val();
    $.ajax({
        url: '{% url "article:article_tag" %}',
        type: "POST",
        data: {"tag":tag},
        success: function(e){
            if(e=="1"){
                layer.msg("You have added a new tag.");
                window.location.reload();
            } else {
                layer.msg(e)
            }
        }
    });
}

function del_tag(the, tag_id){
    var article_tag = $(the).parents("tr").children("td").eq(1).text();
    layer.open({
        type: 1,
        skin: "layui-layer-rim",
        area: ["400px", "200px"],
        title: "删除文章标签",
        content: '<div class="text-center" style="margin-top:20px"><p>是否确定删除文章标签《'+article_tag+'》</p> </div>',
        btn:['确定', '取消'],
        yes: function(){
            $.ajax({
                url: '{% url "article:del_article_tag" %}',
                type:"POST",
                data: {"tag_id":tag_id},
                success: function(e){
                    if(e=="1"){
                        parent.location.reload();
                        layer.msg("The tag has been deleted.");
                    }else{
                        layer.msg("删除失败");
                    }
                },
            })
        },
    });
}

</script>
{% endblock %}

2.4 测试

访问 http://127.0.0.1:8000/article/article-tag 点击删除图标

删除图标

3、发布文章时选择标签

3.1 视图函数

在./article/views.py中修改article_post函数

import json

@login_required(login_url='/account/login')
@csrf_exempt
def article_post(request):
    if request.method == "POST":
        article_post_form = ArticlePostForm(data=request.POST)
        if article_post_form.is_valid():
            cd = article_post_form.cleaned_data
            try:
                new_article = article_post_form.save(commit=False)
                new_article.author = request.user
                new_article.column = request.user.article_column.get(id=request.POST['column_id'])
                new_article.save()
                tags = request.POST['tags']
                if tags:
                    for atag in json.loads(tags):
                        tag = request.user.tag.get(tag=atag)
                        new_article.article_tag.add(tag)
                return HttpResponse("1")
            except:
                return HttpResponse("2")
        else:
            return HttpResponse("3")
    else:
        article_post_form = ArticlePostForm()
        article_columns = request.user.article_column.all()
        article_tags = request.user.tag.all()
        return render(request, "article/column/article_post.html", {"article_post_form": article_post_form, "article_columns": article_columns, "article_tags": article_tags})

3.2 模板文件

修改 ./templates/article/column/article_post.html

{% extends "article/base.html" %}
{% load staticfiles %}
{% block title %}article column{% endblock %}
{% block content %}
<link rel="stylesheet" href="{% static 'editor/css/style.css' %}">
<link rel="stylesheet" href="{% static 'editor/css/editormd.css' %}">

<div style="margin-left:10px">
    <form class="form-horizontal" action="." method="post">{% csrf_token %}
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>标题:</span></div>
            <div class="col-md-10 text-left">{{article_post_form.title}}</div>
        </div>
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>栏目:</span></div>
            <div class="col-md-10 text-left">
                <select id="which_column">
                    {% for column in article_columns %}
                    <option value="{{column.id}}">{{column.column}}</option>
                    {% endfor %}
                </select>
            </div>
        </div>
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>文章标签:</span></div>
            <div class="col-md-10 text-left">
                {% for tag in article_tags %}
                <label class="checkbox-inline">
                    <input class="tagcheckbox" type="checkbox" id="{{ tag.id }}" name="article_tag" value="{{ tag.tag }}"> {{ tag.tag }}
                </label>
                {% empty %}
                <p>You have not type tags for articles. Please <a href="{% url 'article:article_tag' %}">input your tags</a></p>
                {% endfor %}
            </div>
        </div>
        <div class="row" style="margin-top: 10px;">
            <div class="col-md-2 text-right"><span>内容:</span></div>
            <div id="editormd" class="col-md-10 text-left">
                <textarea style="display:none;" id="id_body"></textarea>
            </div>
        </div>
        <div class="row">
            <input type="button" class="btn btn-primary btn-lg" value="发布" onclick="publish_article()">
        </div>
    </form>
</div>

<script type="text/javascript" src='{% static "js/jquery.js" %}'></script>
<script type="text/javascript" src="{% static 'js/layer.js'%}"></script>
<script type="text/javascript" src="{% static 'js/json2.js' %}"></script>
<script type="text/javascript">
function publish_article(){
    var title = $("#id_title").val();
    var column_id = $("#which_column").val();
    var body = $("#id_body").val();
    var article_tags = [];
    $.each($("input[name='article_tag']:checked"), function(){article_tags.push($(this).val());});
    $.ajax({
        url: "{% url 'article:article_post' %}",
        type: "POST",
        data: {"title":title, "body":body, "column_id":column_id, "tags":JSON.stringify(article_tags)},
        success: function(e){
            if(e=="1"){
                layer.msg("successful");
                location.href = "{% url 'article:article_list' %}";
            }else if(e=="2"){
                layer.msg("sorry.") ;
            }else{
                layer.msg("项目名称必须写,不能空。");
            }
        },
    });
}
</script>

<script type="text/javascript" src='{% static "editor/editormd.min.js" %}'></script>
<script type="text/javascript">
    $(function() {
        var editor = editormd("editormd", {
            width : "100%",
            height : 640,
            syncScrolling : "single",
            path : "{% static 'editor/lib/' %}"
        });
    });
</script>

{% endblock %}

访问 http://127.0.0.1:8000/article/article-post/

发布文章时增加标签

3.3 在文章中显示文章标签

修改 ./templates/article/list/article_content.html 在作者、点赞计数那一行的下面增加如下代码

<p> <span style="margin-right: 10px"><strong>Tags:</strong></span> {{ article.article_tag.all | join:", "}}</p>

3.4 测试

访问刚才发表的文章,文章标签已显示在文章详情中了


显示文章标签

4、通过文章标签推荐相似文章

4.1 视图函数

在./article/list_views.py中修改article_detail函数

def article_detail(request, id, slug):
    article = get_object_or_404(ArticlePost, id=id, slug=slug)
    total_views = r.incr("article:{}:views".format(article.id))
    r.zincrby('article_ranking', 1, article.id)

    article_ranking = r.zrange("article_ranking", 0, -1, desc=True)[:10]
    article_ranking_ids = [int(id) for id in article_ranking]
    most_viewed = list(ArticlePost.objects.filter(id__in=article_ranking_ids))
    most_viewed.sort(key=lambda x: article_ranking_ids.index(x.id))

    if request.method == "POST":
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.article = article
            new_comment.save()
    else:
        comment_form = CommentForm()

    article_tags_ids = article.article_tag.values_list("id", flat=True)
    similar_articles = ArticlePost.objects.filter(article_tag__in=article_tags_ids).exclude(id=article.id)
    similar_articles = similar_articles.annotate(same_tags=Count("article_tag")).order_by('-same_tags', '-created')[:4]
    return render(request, "article/list/article_content.html",
                  {"article": article, "total_views": total_views, "most_viewed": most_viewed, "comment_form": comment_form, "similar_articles": similar_articles})

4.2 模板文件

修改 ./templates/article/list/article_content.html 在“最多评论文章”栏目代码的下面增加如下代码

<hr>
        <p class="text-center">
        <h3>推荐相似文章</h3></p>
        <ul>
            {% for similar in similar_articles %}
            <li>
                <p><a href="{{ similar.get_url_path }}">{{ similar.title }}</a></p>
            </li>
            {% empty %}
            <p>Sorry, 没有相似文章</p>
            {% endfor %}
        </ul>

4.3 测试

访问刚才发表的文章,右下角已显示“ 推荐相似文章”栏目


推荐相似文章