【Django】axiosを使って自作Like機能を作ってみた

# はじめに
今回はDjangoで作ったWebサイトにaxiosを使っていいね機能が作れないかと調べてみました。
案の定、Ajaxを使うか画面リロード付きいいね機能の作り方しかなかったので、参考サイトを見ながらaxiosでいいね機能を作ってみました。

## 参考サイト
#### いいね機能を実装したサイトのチュートリアルサイト
https://tutorial.djangogirls.org/ja/
#### いいね機能参考
https://jyouj.hatenablog.com/entry/2018/07/22/232911

## 本題
記事の順序に従って実装していきます。
この機能が欲しい人はそこそこコードを読めると信じているので簡単な説明は省きました。

### Model.py
PostクラスなどはDjango Girls Tutorialのモデルを参照しています。

```python

from django.db import models
from django.conf import settings
from django.utils import timezone
from django.contrib.auth.models import User   #追加


class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_at = models.DateTimeField(default=timezone.now)
    published_date = models.DateTimeField(blank=True, null=True)
    like_num = models.IntegerField(default=0)  #追加

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def __str__(self):
        return self.title

以下コードがいいね機能モデル　↓追加
class Like(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='like_user')
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
```

### View.py
今回はクラスベースで書いていますので、関数ベースで作りたい方は工夫してください
```#like``` ```#unlike```のJsonResponseの引数に関しては渡したいJsonデータを設定してください

```python
class Like_Detail(View):
    def get(self, request, pk, *args, **kwargs):
        post = Post.objects.get(id=pk)
        is_like = Like.objects.filter(user=self.request.user).filter(post=post).count()
        # unlike
        if is_like > 0:
            liking = Like.objects.get(post__id=pk, user=self.request.user)
            liking.delete()
            post.like_num -= 1
            post.save()
            post = get_object_or_404(Post, pk=pk)
            json = {'like_value': post.like_num}     #ここのJsonデータに関してはご自由に
            return JsonResponse(json)
        # like
        post.like_num += 1
        post.save()
        like = Like()
        like.user = self.request.user
        like.post = post
        like.save()
        post = get_object_or_404(Post, pk=pk)
        json = {'like_value': post.like_num}         #ここのJsonデータに関してはご自由に
        return JsonResponse(json)
```

### URL.py
```django


from django.urls import path
from . import views
from .views import Like_Detail　# View.pyで設定したクラス名をインポートさせてください

app_name = 'application'    #アプリケーション名で合わせてください
urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
    path('post/new/', views.post_new, name='post_new'),
    path('post/<int:pk>/edit/', views.post_edit, name='post_edit'),
    path('post/<int:pk>/like/',Like_Detail.as_view(), name='get'),   #追加
]
```

### POST_Detail.html
最後に一番重要なHTML内のJavaScript処理について説明します。
##### < 記事サイトに飛んだ時の処理の流れです >
1.記事詳細ページに飛んだClientユーザーの情報をView.pyに渡す様に命令をします。
2.Javascript側にView.py側から渡されたClientユーザーがいいねしているか、していないかと記事の総合いいね数の情報を受け取ります。
3.もし、記事に対してユーザーがいいねをしていない場合は```<input>```タグのclassに```unlike```タグを追加します。(いいねしている場合は```like```を追加させる処理が書かれています。)

##### < いいねボタンが押された際の処理の流れです >
1.いいねボタンを押すとaxiosを利用して画面リロードなしてリクエストを送ります。
2.View.py側でユーザーの記事のいいね処理を行います。
3.HTML側ではJavascriptが押されたら数値を変更するので毎回View.py側からデータを受け取る必要がなくなることになります



```html

{% extends 'application/base.html' %}

{% block content %}
    <div class="post">
        {% if post.published_date %}
            <div class="date">
                {{ post.published_date }}
            </div>
        {% endif %}
        {% if user.is_authenticated %}
            <a class="btn btn-default" href="{% url 'application:post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
            <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
            <input type="button" value="Check" onclick="like()" id="like-data"> いいね<span id="like-count">{{ post.like_num }}</span>
            <script>
                var likeinfo = {{ is_like }};
                var likecount = {{ post.like_num }};
                window.onload = function () {
                    if (likeinfo > 0){
                        document.getElementById("like-data").classList.add("unlike");
                    }else{
                        document.getElementById("like-data").classList.add("like");
                    }
                };
                var like = function () {
                    const response = axios.get('/post/{{ post.pk }}/like/');
                    console.log(response);
                    console.log( document.getElementById('like-count').innerText);
                    if (likeinfo > 0){
                        likeinfo = likeinfo - 1;
                        likecount = likecount - 1;
                        document.getElementById('like-count').innerText=likecount;
                    }else{
                        likeinfo = likeinfo + 1;
                        likecount = likecount + 1;
                        document.getElementById('like-count').innerText=likecount;
                    }
                        document.getElementById("like-data").classList.toggle("unlike");
                        document.getElementById("like-data").classList.toggle("like");
                }
            </script>
        {% endif %}
        <h2>{{ post.title }}</h2>
        <p>{{ post.text|linebreaksbr }}</p>
    </div>
{% endblock %}
```
### 補足 CSS

##### ```<input>```タグのclassに```like```や```unlike```をタグで付け替える意味は```いいねしている```か、```してない```かでCSSのデザインを変更する設定をしているからです。

```css
.unlike {
    color:red;
}
.like {
    color: black;
}
```

## 最後に
自分用に書いてはいますが、
参考になりましたらいいね👍よろしくお願いします。
色んな人に見ていただければ次回allauthに関しても書こうかなと思います。