【連載】SAPを作成  = Django × RestFramework × Vue.cli 　

# まえがき

タイトルの通り、Django × RestFramework × Vue.cliを使ってSAPを作ってみたいと思います。
練習なので、至らない点があると思いますが、コメント等でアドバイスいただけると嬉しいです:relaxed:

# 環境
Django3系
python3.8
restframework
Vue.cli v2

# 作るもの
よくチュートリアルである書籍管理アプリを作ってみます。

#STEP1:APIの作成

まずはファイル構造は以下の通り

```
root
├── Book
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── api
│   ├── Serializer.py
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── frontend
│   ├── README.md
│   ├── babel.config.js
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   └── src
├── manage.py
├── templates
├── venv
│   ├── bin
│   ├── include
│   ├── lib
│   └── pyvenv.cfg
└── vue_sample
    ├── __init__.py
    ├── __pycache__
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

```

### model用としてBookアプリを作成ました。

1. Bookモデルには、タイトルと説明,関連にはpublisher（出版社モデル）を紐付けています。
1. Publisherモデルは出版社名のみ
1. Authorには著者名とbookモデルをmany to manyで紐付けます。

```py
from django.db import models


class Publisher(models.Model):
    pub_name = models.CharField('出版社', max_length=255, blank=True)

    def __str__(self):
        return self.pub_name


class Book(models.Model):
    title = models.CharField('タイトル',max_length=20, blank=True)
    description = models.TextField('説明', max_length=500, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title


class Author(models.Model):
    author_name = models.CharField('著者名', max_length=255, blank=True)
    book = models.ManyToManyField(Book, blank=True)

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

### Serializerの作成

apiアプリに移動し、Serializerを作成します。

```api/serializer.py
from rest_framework import serializers
from Book.models import Book, Publisher, Author


class PublisherSerializer(serializers.ModelSerializer):

    class Meta:
        model = Publisher
        fields = '__all__'


class AuthorSerializer(serializers.ModelSerializer):

    class Meta:
        model = Author
        fields = '__all__'


#authorの一覧を取得したいので,ListSerializerを継承し、child変数に,AuthorSerializerを渡します
class AuthorListSerializer(serializers.ListSerializer):

    child = AuthorSerializer()


class BookSerializer(serializers.ModelSerializer):
     # many to manyで紐付いている、Author一覧を動的に表示させるため、SerializerMethodField()を使用します。
     #　author変数は,fieldsに追加することで、modelには有りませんが、追加することが可能です。
    author = serializers.SerializerMethodField()
    #responseにidではなく、オブジェクトで返す場合は,PublisherSerializerを上書きする。
    publisher = PublisherSerializer()


    class Meta:
        model = Book
        fields = ['id', 'title', 'description', 'created_at', 'publisher', 'updated_at', 'author']
   
    #SerializerMethodField()を使用すると必要になるのが、以下の関数。
    # get_<変数名>(self, obj)とする。

    def get_author(self, obj):
        try:
            author_child = AuthorSerializer(Author.objects.all().filter(book=Book.objects.get(id=obj.id)),many=True).data
            return author_child
        except:
            author_child = None
            return author_child



```

### viewの作成

今回は楽をするためCRUDのフルセットがかんたんに実現できるViewSetを使います。

```api/views.py

from .Serializer import *
from rest_framework.viewsets import ModelViewSet
from Book.models import Book


class BookAPIView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

```

### URLの作成

まずは、apiアプリをプロジェクトurlsにインクルードします。

```プロジェクト/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('api.urls')),
]
```

つづいて、apiアプリのurlを作成
Viewsで使用したModelViewSetを使用した場合はrestframeworkのrouterをインポートしてurlを定義します。

```api/urls.py
from django.urls import path, include
from rest_framework import routers #追記
from .views import BookAPIView

router = routers.DefaultRouter()
router.register('books', BookAPIView) #第一引数がurl,第二引数がviewsのクラス

urlpatterns = [
    path('', include(router.urls))
]
```

一旦以上でAPIは作成完了です。

# 動作確認

`http://127.0.0.1:8000/api/v1/`ルートにアクセス。
![スクリーンショット 2021-04-23 22.29.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/dbdbe8b5-9894-2369-1aa8-80ed5ca64e0d.png)

つずいて、apiのurlsにアクセス。
`http://127.0.0.1:8000/api/v1/books/`にアクセス


![スクリーンショット 2021-04-23 22.31.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/8b66168d-0599-05a3-3152-7fc6e8a20d11.png)
![スクリーンショット 2021-04-23 22.31.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/68a90720-5d81-01a5-280f-9327b404ab94.png)

事前に管理画面で登録した情報が取得できました。
投稿も、一覧も表示できましたね。

編集、削除する場合は、urlをいかに変更する。

#### 編集,削除
`http://127.0.0.1:8000/api/v1/books/1/`
urlにbookのidを追加すると画面が以下に変わる。

![スクリーンショット 2021-04-23 22.33.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/c81f7ae2-7eff-c91f-db45-3b7f3a9c5f49.png)

編集も削除も利用できますね。簡単にできました。

次回は,Vue cliを導入します。

ありがとうございました。