【Django REST Framework + Vue.js】APIからデータを取得し表示する

# はじめに
　ブログや投稿系のアプリを作成するとを考え、Django REST FrameworkとVue.jsを使って、投稿一覧データを取得し画面に表示させるまでの流れを扱いたいと思います。
　大まかな流れは以下の通りです。
　1. Django REST Frameworkで投稿一覧APIを作成する。
　2. フロントのVue.jsからaxiosによってAPIからデータを取得し画面に表示する。

# 1. Django REST Frameworkの記述
　まず、Django REST Frameworkをインストールしsettings.pyのINSTALLED_APPSにrest_frameworkの追加をしてください。
　Djangoの設定ディレクトリ名を「config」, アプリケーションディレクトリ名を「apiv1」としています。

### models.pyの設定
今回はCategoryモデルとPostモデルと作成しました。

```python:apiv1/models.py
from django.db import models
from django.contrib.auth import get_user_model

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.name


class Post(models.Model):
    category = models.ForeignKey(to=Category, on_delete=models.CASCADE)
    author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    content = models.TextField()
    published_at = models.DateTimeField(auto_now_add=True)
   
    class Meta:
        ordering = ['-published_at']

    def __str__(self):
        return self.title
```

### serializers.pyの設定

```python:apiv1/serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import Category, Post

class UserSerializer(serializers.ModelSerializer):
    """ユーザーシリアライザー"""
    class Meta:
        model = get_user_model()
        fields = '__all__'


class CategorySerializer(serializers.ModelSerializer):
    """カテゴリシリアライザ"""
    class Meta:
        model = Category
        fields = ('id', 'name')


class PostSerializer(serializers.ModelSerializer):
    """投稿シリアライザ"""
    class Meta:
        model = Post
        fields = '__all__'
```
### views.pyの設定
　Viewの指定方法はいくつか種類がありますが、今回は自分が一番わかりやすいと感じたgeneric viewを使いたいと思います。
　generic viewは以下のコードにあるようなListAPIView（一覧取得）の他に、CreateAPIView（作成）やRetrieveAPIView（詳細取得）、UpdateAPIView（更新）などがあります。他にもたくさん種類があり、作成, 取得, 削除, 更新によって使い分けることができます。ListCreateAPIViewという一覧取得と作成を行ってくれるものもあります。

```python:apiv1/views.py
from rest_framework import generics
from django.contrib.auth import get_user_model
from .models import Post, Category
from .serializers import CategorySerializer, PostSerializer

class CategoryListAPIView(generics.ListAPIView):
    """カテゴリモデルの取得（一覧）APIクラス"""
    queryset = Category.objects.all()
    serializer_class = CategorySerializer


class PostListAPIView(generics.ListAPIView):
    """投稿モデルの取得（一覧）APIクラス"""
    queryset = Post.objects.all()
    serializer_class = PostSerializer
```

### urls.pyの設定
```python:config/urls.py（プロジェクトのurls.py）
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('apiv1.urls')),
]
```
```python:apiv1/urls.py
from django.urls import path
from . import views

app_name = 'apiv1'
urlpatterns = [
    path('categories/', views.CategoryListAPIView.as_view()),
    path('posts/', views.PostListAPIView.as_view()),
]
```
　この時点でスーパーユーザーを作成した後admin管理画面からカテゴリおよび投稿をいくつか作成し、 http://127.0.0.1:8000/api/v1/categories/, http://127.0.0.1:8000/api/v1/posts/ にアクセスするとそれぞれ以下のような表示になると思います。

http://127.0.0.1:8000/api/v1/categories/ 
![スクリーンショット 2020-08-11 1.53.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/e76b5c94-087f-e441-d119-0fbc3c00f164.png)
http://127.0.0.1:8000/api/v1/posts/
![スクリーンショット 2020-08-11 1.43.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/16b3a948-b327-c6bb-08b2-7c91c193f2bb.png)

これらのデータをVue.js側で呼び出し、表示させたいと思います。

# 2. Vue.jsの記述
　まず最初にaxios, vue-axiosのインストールをしてmain.jsに設定を記述します。

```javascript:src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios' //追記
import VueAxios from 'vue-axios' //追記

Vue.config.productionTip = false

Vue.use(VueAxios, axios) //追記

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
```
次にVueRouterを使ってパスを指定します。
今回は http://127.0.0.1:8080/postlist と指定されたら、PostList.vueが表示されるように設定しました。

```javascript:src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import PostList from '@/components/PostList'

Vue.use(VueRouter)

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/postlist',
      component: PostList
    },
  ]
})

```
PostList.vueファイルに記述していきます。

```vuejs:src/components/PostList.vue
<template>
  <div>
    <div v-for="(post, key) in Posts" :key="key">
      <hr>
      <p>カテゴリ: {{ post.category }}</p>
      <p>タイトル: {{ post.title }}</p>
      <p>内容: {{ post.content }}</p>
      <p>投稿日: {{ post.published_at}}</p>
      <hr>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      Posts: []
    };
  },
  mounted() {
    this.axios
      .get("http://127.0.0.1:8000/api/v1/posts/")
      .then(response => (this.Posts = response.data));
  }
};
</script>

```
http://127.0.0.1:8080/postlist にアクセスすると次の様に表示できました。
![スクリーンショット 2020-08-11 2.16.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/296b730c-3ffa-6d94-8b19-ebbc09a3b20c.png)
　今の段階ではカテゴリが数字（id）で表現されています。今回は文字を表示して欲しいので、Djangoのserializers.pyに追記していきます。
　PostSerializerに`category = CategorySerializer()`を追記してカテゴリ名が取得できるようにします。

```python:apiv1/serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import Category, Post

class UserSerializer(serializers.ModelSerializer):
    """ユーザーシリアライザー"""
    class Meta:
        model = get_user_model()
        fields = '__all__'


class CategorySerializer(serializers.ModelSerializer):
    """カテゴリシリアライザ"""
    class Meta:
        model = Category
        fields = ('id', 'name')


class PostSerializer(serializers.ModelSerializer):
    """投稿シリアライザ"""
    category = CategorySerializer() #追記

    class Meta:
        model = Post
        fields = '__all__'
```
そのあともう一度 http://127.0.0.1:8000/api/v1/posts/ にアクセスすると、以下のようになると思います。
　CategoryとPostが1対多の関係でありForeignKeyで結び付けられているためcategoryの正体を見ることができました。
![スクリーンショット 2020-08-11 2.21.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/0fc45694-5cb7-b5b6-144c-86e7d15b560e.png)
　
　最後にPostList.vueの`<p>カテゴリ: {{ post.category }}</p>`の部分を`<p>カテゴリ: {{ post.category.name }}</p>`と変更し http://127.0.0.1:8080/postlist にアクセスすると、以下の様にカテゴリ名が正しく表示されるようになりました。
![スクリーンショット 2020-08-11 2.27.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/5dd81e31-721b-d29a-7cf9-19dd331e67f4.png)

　以上がDjangoRestFrameworkとVueを用いてAPIからデータを取得、表示する大まかな流れでした。