どうも、前回の記事後編をサボっている月歩人です。
今回はDjango使いがAPIを作成する場合に大人気のDjangoRestFlamework(移行DRF)について、
ちょっとハマってしまったが意外に調査に時間がかかった点をtipsとして記事にします。
条件は以下です。
- 特定のフィールドがUNIQUEである
- 特定のフィールドがPOSTされなかったらデフォルト値をuuidで入れたい
- APIエンドポイントは
api/test/
サクッとサンプルプロジェクトを作成
尚、DRFの導入とurls.pyとsettings.py等の設定は省略します。
from django.db import models
class TestModel(models.Model):
code = models.UUIDField(unique=True)
def __str__(self):
return self.code
from rest_framework import viewsets
from .models import TestModel
from .serializer import TestModelSerializer
class TestModelViewSet(viewsets.ModelViewSet):
queryset = TestModel.objects.all()
serializer_class = TestModelSerializer
import uuid
from rest_framework import serializers
from .models import TestModel
class TestModelSerializer(serializers.ModelSerializer):
code = serializers.UUIDField(default=uuid.uuid4())
class Meta:
model = TestModel
fields = ('id', 'code')
はい、こんな感じです。
これでTestModelのCRUD APIができました。
DRFは神
しかし、感の良い方はお気づきでしょう。
このコードには致命的なバグがあります。
とりあえず動かす
$ python manage.py runserver
そうです。
2回目はUNIQUEエラーになってしまうのです。
1回目はできたのに…
原因
...
code = serializers.UUIDField(default=uuid.uuid4())
...
uuid.uuid4()
という記述をしてしまうことで、runserver
時点でuuidが生成されてデフォルトとして設定されてしまった。
なのでこの場合は内部的に
...
code = serializers.UUIDField(default='492e32fc-dce5-4ac7-b13d-0b3af0236f75')
...
という動作になってしまっていたのです。
生成するたびにuuid作成すると思う人私以外にもいると思う(いてください)
対策
uuid.uuid4をコールバック関数として渡す
...
code = serializers.UUIDField(default=uuid.uuid4)
...
こうすることで、runserver
時点でcodeのdefaultはuuid.uuid4
というコールバック関数を持つ為、
空で登録する際にコールバック関数が走り、uuidが生成されるので一件落着ということだ。
「知っているようで知らなかった部分」、「Documentにひっそり書かれている事を飛ばしてしまいハマった」という問題はエンジニアあるあるだと思う。