4.3 为文章点赞

“点赞”这个词语不知道从什么时候开始逐渐替代了“赞赏”“称赞”等词汇,下面就实现对文章点赞的功能,如下图所示。

4.3.1 修改数据模型类

编辑熟悉的ArticlePost数据模型类,它在./article/models.py文件中,在其众多字段中增加如下代码。

1 users_like = models.ManyToManyField(User,on_delete=models.CASCADE,related_name="articles_like",blank=True) #①

关注语句①中的一个新的字段类型ManyToManyField。在本书前面的章节中,已经出现过OneToOneField和ForeignKey两种字段类型。以上三个字段类型都是用于定义具有关联关系字段的,但它们之间有所差别。

很显然,一篇 文章可以被多个用户阅读并点赞,而每个用户可以给多篇文章点赞,这就是所谓的“多对多”关系。

将语句①添加到文件中后,要及时完成数据迁移,即执行python manage.py makemigrations和python manage.py migrate。然后打开Friefox浏览器,用前面使用过的SQLite Manager查看当前的数据库状态,如下图所示。

可以看到,数据库中多了一个名为article_articlepost_users_like的表(并没有像ArticlePost数据模型类中的其他属性那样,在article_articlepost表中增加一个users_like字段),这个表的字段有两个:articlepost_id和user_id。这说明语句①的作用就是建立起两个数据模型(数据库表)的对应关系。

4.3.2 编写视图函数

数据模型确定好之后,就有了保存数据的数据库表了,接着编写视图函数,完成用户的操作行为。

在./article/list_views.py文件中引入下列代码的内容。

1 from django.contrib.auth.decorators import login_required
2 from django.views.decorators.csrf import csrf_exempt
3 from django.views.decorators.http import require_POST
4 from django.http import HttpResponse

上述三个方法都是我们熟悉的装饰器,接下来编写如下代码。

 1 @csrf_exempt
 2 @require_POST
 3 @login_required(login_url='/account/login')
 4 def like_article(request):
 5     article_id = require.POST.get("id") #①
 6     action = require.POST.get("action") #②
 7     if article_id and action:
 8         try:
 9             article = ArticlePost.objects.get(id=article_id)
10             if action=="like":
11                 article.users_like.add(request.user) #③
12                 return HttpResponse("1")
13             else:
14                 article.users_like.remove(request.user) #④
15                 return HttpResponse("2")
16         except:
17             return HttpResponse("no")

语句①和语句②得到前端以POST方式传过来的两个值,显然这里已经规定好了前端必须用类字典的形式向视图函数提交id和action的值。

语句③用于给article这个实例的属性users_like增加一个用户,即该用户“点赞”了此文章。如果不同对象之间建立了一对多或者多对多的关联关系,那么就可以使用add(*objs,bulk=True)方法增加属性的值,从而建立起两个对象的关系。例如语句③中的add()方法,就将article实例对象和user实例对象之间建立了关联,如果这一步成功执行,那么在数据库表article_articlepost_users_like中就能够看到article和user各自的id值。以上是多对多关系,如果是一对多(ForeignKey)关系,同样可以使用add()方法,在Django的官方文档中有一个举例,可以赏析(https://docs.djangoproject.com/en/1.10/ref/models/relations/#django.db.models.fields.related.RelateManager.add)。

语句④与语句③相反的操作,其使用方法和add()类似,即把相关联的对象的关系删除。

完成了视图函数的编写,就可以在./article/urls.py中配置URL了,增加如下代码。

1 path('like-article/',list_view.like_article,name="like_article"),

4.3.3 修改模板文件

不需要重新编写模板文件,只要在原来展示文章详细内容的模板中增加“点赞”相关的内容即可。./templates/article/list/article_detail.html文件的全部代码如下。

 1 {% extends "base.html" %}                                                                                                                                                                                      
 2 {% load staticfiles %}                                                                                                                                                                                         
 3 {% block title %}{{ article.title }}{% endblock %}                                                                                                                                                             
 4 {% block content %}                                                                                                                                                                                            
 5                                                                                                                                                                                                                
 6 {% with total_likes=article.users_like.count users_like=article.users_like.all %}   #①                                                                                                                           
 7 <div class="container">                                                                                                                                                                                        
 8     <header>                                                                                                                                                                                                   
 9         <h1>{{ article.title }}</h1>                                                                                                                                                                           
10         <p>                                                                                                                                                                                                    
11             <a href="{% url 'article:author_articles' article.author.username %}">                                                                                                                             
12                 {{ article.author.username }}                                                                                                                                                                  
13             </a>                                                                                                                                                                                               
14             <span style="margin-left:20px" class="glyghicon glyphicon-thumbs-up">{{ total_likes }}like{{ total_likes | pluralize }}</span>       #②                                                              
15         </p>                                                                                                                                                                                                   
16     </header>                                                                                                                                                                                                  
17                                                                                                                                                                                                                
18     <link rel="stylesheet" href='{% static "editor/css/editormd.preview.css" %}' />                                                                                                                            
19     <div id="editormd-view">                                                                                                                                                                                   
20         <textarea id="append-test" style="display:none;">                                                                                                                                                      
21 {{ article.body }}                                                                                                                                                                                             
22         </textarea>                                                                                                                                                                                            
23     </div>                                                                                                                                                                                                     
24     <div>                                                                                                                                                                                                      
25         <p class="text-center">                                                                                                                                                                                
26             <a onclick="like_article({{article.id}}, 'like')" href="#"><span class="glyghicon glyphicon-thumbs-up">like</span></a>   #③                                                                          
27             <a onclick="like_article({{article.id}}, 'unlike')" href="#"><span style="margin_left:15px;" class="glyghicon glyphicon-thumbs-down">unlike</span></a>   #④                                          
28         </p>                                                                                                                                                                                                   
29     </div>                                                                                                                                                                                                     
30     <div>                                                                                                                                                                                                      
31         <p class="text-center"><strong>点赞文本的读者</strong></p>                                                                                                                                                    
32         {% for user in article.users_like.all %}   #⑤                                                                                                                                                   
33         <p class="text-center">{{ user.username }}</p>                                                                                                                                                         
34         {% empty %}                                                                                                                                                                                            
35         <p class="text-center">还没有人对此文章表态</p>                                                                                                                                                                  
36         {% endfor %}                                                                                                                                                                                           
37     </div>                                                                                                                                                                                                     
38                                                                                                                                                                                                                
39 </div>                                                                                                                                                                                                         
40 <script src="{% static 'js/jquery-3.3.1.js' %}"></script>                                                                                                                                                      
41 <script src="{% static 'editor/lib/marked.min.js' %}"></script>                                                                                                                                                
42 <script src="{% static 'editor/lib/prettify.min.js' %}"></script>                                                                                                                                              
43 <script src="{% static 'editor/lib/raphael.min.js' %}"></script>                                                                                                                                               
44 <script src="{% static 'editor/lib/underscore.js' %}"></script>                                                                                                                                                
45 <script src="{% static 'editor/lib/sequence-diagram.min.js' %}"></script>                                                                                                                                      
46 <script src="{% static 'editor/lib/flowchart.min.js' %}"></script>                                                                                                                                             
47 <script src="{% static 'editor/lib/jquery.flowchart.min.js' %}"></script>                                                                                                                                      
48 <script src="{% static 'editor/editormd.js' %}"></script>                                                                                                                                                      
49 <script type="text/javascript" src="{% static 'js/layer.js'%"></script>  #⑥                                                                                                                                      
50                                                                                                                                                                                                                
51 <script type="text/javascript">                                                                                                                                                                                
52     $(function(){                                                                                                                                                                                              
53         editormd.markdownToHTML("editormd-view",{                                                                                                                                                              
54             htmlDecode:"style,script,iframe",  // you can filter decode                                                                                                                                        
55             emoji:true,                                                                                                                                                                                        
56             taskList:true,                                                                                                                                                                                     
57             tex:true,  //默认不解析                                                                                                                                                                                 
58             flowChart:true,  //默认不解析                                                                                                                                                                           
59             sequenceDiagram:true,  //默认不解析                                                                                                                                                                     
60         });                                                                                                                                                                                                    
61     });                                                                                                                                                                                                        
62                                                                                                                                                                                                                
63     function like_article(id,action){   #⑦                                                                                                                                                                       
64         $.ajax({                                                                                                                                                                                               
65             url:"{% url 'article:like_article' %}",                                                                                                                                                            
66             type:"POST",                                                                                                                                                                                       
67             data:{"id":id,"action":action},                                                                                                                                                                    
68             success:function(e){                                                                                                                                                                               
69                 if(e=="1"){                                                                                                                                                                                    
70                     layer.msg("感谢点赞");                                                                                                                                                                         
71                     window.location.reload();                                                                                                                                                                  
72                 }else{                                                                                                                                                                                         
73                     layer.msg("我会继续努力");                                                                                                                                                                       
74                     window.location.reload();                                                                                                                                                                  
75                 }                                                                                                                                                                                              
76             },                                                                                                                                                                                                 
77         });                                                                                                                                                                                                    
78     }                                                                                                                                                                                                          
79 </script>                                                                                                                                                                                                      
80 {% endwith %}                                                                                                                                                                                                  
81 {% endblock %}                                                                                                                                                                                                 

 

 语句①包含两部分,一部分是{% with ...%},另一部分是结尾的{% endwith %},这里使用with发起了一个赋值操作,total_likes=article.users_like.count 和users_like=article.users_like.all分别为点赞用户总数和用户对象。这样,在两部分所圈定的区间中就可以使用变量total_likes和users_like。请读者学习并掌握这种模板语法。

语句②中增加了<span style="margin-left:20px" class="glyghicon glyphicon-thumbs-up">{{ total_likes }}like{{ total_likes | pluralize }}</span>,需要解释的是{{ total_likes | pluralize }}。当没有用户“点赞”或者超过1个用户“点赞”时,显示的是likes这种复数形式;如果只有一个用户“点赞”,则显示单数形式like。这就是{{ total_likes | pluralize }}的作用。

语句③④都是新增的内容,在文章内容的末尾增加“点赞”的操作,而“点赞”功能的实现则是通过JavaScript函数(语句⑦)来实现的。提醒读者小心的是,在语句⑦中我们又使用了前面熟悉的弹出层的插件,所以要增加语句⑥。

语句⑤是另外一种模板语法的应用。对于for循环,我们已经不陌生了。如果循环对象是空(bool()的值为False),则可能在循环之前用if来判断,这里使用{% empty %},当然,不要忘记最后用{% endfor %}结束,也就是我们在这里使用了{% for ...%}...{% empty %}...{% endfor %}的语法结构。

“点赞”功能完成了,看看效果吧。

如下图所示是没有用户“点赞”的效果。

 读者注意观察上图中的likes目前是复数形式,当用户“点赞”这篇文章之后(刷新),效果如下图所示。

下面看一下数据库中的存储结果,如下图所示。

数据库中记录了本次操作的数据。

读者还可以继续测试,单击“unlike”或者换一个用户再次单击“like”等。

4.3.4 知识点

1、模型:“多对多”

本节在ArticlePost类中增加了一个字段(users_like =models.ManyToManyField(User,related_name="articles_like",blank=True)),使ArticlePost和User之间建立了“多对多(ManyToMany)”关系。每篇文章可以被多个用户点赞,而每个用户可以对多篇文章点赞,这就是ArticlePost和User之间的关系,这种关系被称为“多对多”关系。她跟以前遇到的“一对多”关系一样,也是两个数据模型类之间的关系。

要将两个数据模型类建立“多对多”的关系,只需要在其中一个类中声明一个字段即可,ORM会自动为另一个类生成使用这种关系的必要的方法和属性(生成一个_set manager对象)。又因为这种“多对多”是对称关系的,所以在哪一个类中声明都可以。例如在本节中,是在ArticlePost中声明了users_like。

在Django的交互模式,可以看看这种关系的基本特点,代码如下。

article.users_like.all()查询所有对本article实例点赞的User实例(对某文章点赞的所有用户);one_user.article_like.all()查询本one_user实例所点赞的ArticlePost实例(某用户点赞过的所有文章),请注意,因为有users_like=models.ManyToManyField(User,related_name="articles_like",blank=True)中的related_name的定义,才能使用one_user.article_like.all()。

“多对多”关系建立之后,在数据库中建立了名为“article_articlepost_users_like”的数据库表,数据库表中保存的是双方的id。

2、模板:with

with是模板的一个内置标签,它的使用方法类似于赋值语句,例如本节中的实例:

{% with total_likes=article.users_like.count users_like=article.users_like.all %}
<!--{{ total_likes }}相关代码-->
{% endwith %}

在变量中使用article.users_like.count,其拼写太长,不方便,不如使用{{ total_like }}更方便,这就如同在Python中把一个对象赋值给一个简单的变量,更便于使用。注意,这个变量的有效范围在{% with %}和{% endwith %}之间。

3、模板:for ... empty

前面对模板的for标签已经有所介绍,现在补充介绍for...empty标签。有时候我们会遇到下面的代码(以下实例来自官网)

<ul>
    {% if athlete_list %}
        {% for athlete in athlete_list %}
            <li>{{ athlete.name }}</li>
         {% endfor %}
    {% else %}
            <li>Sorry,no athletes in this list.</li>
    {% endif %}
</ul>

用if来判断某个变量是否为空,如果不为空,就循环。因为这种判断在模板中经常会遇到,所以Dajngo提供了一种名为for...empty的标签,它的使用方法是:

<ul>
    {% if athlete_list %}
        <li>{{ athlete.name }}</li>
    {% empty %}
            <li>Sorry,no athletes in this list.</li>
    {% endfor %}
</ul>

 

posted @ 2019-05-21 15:46  taoziya  阅读(226)  评论(0)    收藏  举报