Web APIつくってみた

## はじめに
本記事は[Volare Advent Calendar 2019](https://qiita.com/advent-calendar/2019/volare)の21日目の担当分です。

今回初めてiOSアプリのAPIを作ってみました。
自分が勉強中なのもあって、DjangoRESTFrameworkを使ってAPIをつくってみたので、それを記事にしていきたいと思います:relaxed:
作ったアプリのiOS側の記事は[こちら](https://qiita.com/okudaryuya/items/bae673b74190bad5e822)

## アプリについて


>アプリ作成においてメインのターゲットは私達のような一人暮らしの大学生です:dark_sunglasses:
アプリの決定した経緯については、
自炊をする際に献立を考えるのが面倒であるが、既存のアプリは冷蔵庫にあるものを参照して検索をかけている
→既存のもののターゲットは家族をもつ人向けである

>好き。嫌いを入力して自動的に献立を提示してくれるアプリがあると便利じゃんと感じた！
→基本的に作り置きはあまりしない、好きなものを好きな時に作りたい（冷蔵庫に食材があまり入っていない:ghost:）

>このような要点から作ることを決めました！（コンセプトが同じものがないなら作ってしまおう:relaxed:）

一緒にチーム開発を行なったりゅーちゃんがアプリについて説明してくれています。
今回はこちらのバックエンドを担当しました。

## 実装したこと
バックエンドでは、以下の5つの機能を実装しました！

* ユーザー認証
* ユーザごとの食材一覧表示
* 食材の選択状態の一括変更
* 食材のランダム表示

このうち、3つを担当したので、その部分について書いていきます！

### ユーザー認証

``` python
from main.models import User, FoodConfigParam
from main.serializers import AuthSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
import random

from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

class RegisterAuthView(GenericAPIView):
    permission_classes = ()
    serializer_class = AuthSerializer
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # uuidが登録されていたらtokenを返す
        if User.objects.filter(uuid=serializer.data['uuid']):
            user = User.objects.get(uuid=serializer.data['uuid'])
            payload = jwt_payload_handler(user)
            return Response({
                'token': jwt_encode_handler(payload),
            })

        user = User.objects.create_user(uuid=serializer.data['uuid'])
        user.save()
        if not user:
            raise AuthenticationFailed()
        payload = jwt_payload_handler(user)
        # ユーザー登録をする時に、デフォルトの食材を追加する
        user = User.objects.filter(uuid=serializer.data['uuid']).last()
        food = FoodConfigParam()
        food.create_defaultfood(user=user)
        return Response({
            'token': jwt_encode_handler(payload),
        })
```
送られてきたUUIDと一致するユーザーがDBにあるかどうかをみて、なければユーザーを作成しています。

### ユーザごとの食材一覧表示と一括変更

``` python
from main.models import FoodConfigParam
from main.serializers import FoodConfigParamSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.views import APIView
from rest_framework.response import Response

class UserFoodConfig(GenericAPIView):
    queryset = FoodConfigParam.objects.all()
    serializer_class = FoodConfigParamSerializer

    def get(self, request):
        foodConfigParams = FoodConfigParam.objects.filter(user=request.user)
        serializer = FoodConfigParamSerializer(foodConfigParams, many=True)
        return Response({'data': serializer.data})

    def put(self, request):
        res = []
        for config in request.data['data']:
            name = config['name']
            rate = config['rate']
            # 登録ユーザーと食材名が一致するものを抽出
            foodConfigParam = FoodConfigParam.objects.get(user=request.user, name=name)
            # 確率を変更
            serializer = FoodConfigParamSerializer(foodConfigParam, data={'rate': rate}, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()
            res.append(serializer.data)
        return Response({'data': res})
```
ユーザーと食材名が一致するものを抽出し、出現率を変更するようにしました。

## 難しかったこと
つくってみたAPIがうまく動かなかったり、動いたけどiOSと繋げた時に予想してない動きになったり、
iOS側では極力ロジックを書かないようにしたり、通信回数を減らしたりと色々あるみたいで、、
もっとフロント側の知識もちゃんと持っておかないと意思疎通ができなくて大変なことになるなと感じました・・・
iOS側と相談して、こうすればやりやすいとかを話し合いながら進めていって、なるほど〜と勉強になりました。

## おわりに
今回はとりあえず動くもの、って作っていたので、RESTfulがいまだにわからずにいます:cry:
なので、django-rest-frameworkの良さをちゃんと知って、良さを活かせるように勉強しようと思いました。
最後まで読んでくださり、ありがとうございました。