Django REST Frameworkでは、 DEFAULT_PARSER_CLASSES
の設定により、グローバルで使われるParserが決まります。
Parsers - Django REST framework
ただ、その初期値をうっかり忘れていたので、メモを残します。
目次
環境
うっかりしていたこと
Django REST Frameworkの DEFAULT_PARSER_CLASSES
ですが、デフォルトでは以下のとおりです。
# https://github.com/encode/django-rest-framework/blob/3.11.0/rest_framework/settings.py#L33 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser' ],
そのため、デフォルトでは、JSONの他に普通のHTMLフォームからの送信(application/x-www-form-urlencoded
)も受け付けることをうっかり忘れていました。
動作確認
アプリ
普通のHTMLフォームからの送信も受け付けることを確認します。
以下のようなDjangoとDjango REST Frameworkを使ったHTMLフォームを用意します。
API側
モデル
from django.db import models class Apple(models.Model): name = models.CharField('品種名', max_length=30, unique=True)
シリアライザ
class AppleSerializer(serializers.ModelSerializer): class Meta: model = Apple fields = '__all__'
ビュー
class AppleViewSet(viewsets.ModelViewSet): queryset = Apple.objects.all() serializer_class = AppleSerializer
HTML側
テンプレート
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Form</title> </head> <body> <form method="post" action="{% url 'parser_classes_api:apple-list' %}"> {% csrf_token %} <label for="name">品種</label> <input type="text" name="name" id="name"> <button type="submit">送信</button> </form> </body> </html>
全体のルーティング
router = DefaultRouter() router.register(r'multi-parser', AppleViewSet) router.register(r'json-only', AppleJsonOnlyViewSet) urlpatterns = [ # HTMLの表示 path('', TemplateView.as_view(template_name='index.html'), name='index'), # API path('api', include(router.urls)), ]
動作
フォームに入力します。
送信ボタンを押すと、Django REST FrameworkのThe Browsable APIの画面に遷移し、POSTが成功したことが分かります。
テストコード的には、以下のコードがすべてパスします。
@pytest.mark.django_db class TestAppleViewSet: def test_formをPOSTする(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:apple-list'), content_type='application/x-www-form-urlencoded', data=urlencode(data)) assert actual.status_code == HTTPStatus.CREATED def test_JSONをPOSTする_content_typeを指定(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:apple-list'), content_type='application/json', data=json.dumps(data)) assert actual.status_code == HTTPStatus.CREATED def test_JSONをPOSTする_formatを指定(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:apple-list'), format='json', data=data) assert actual.status_code == HTTPStatus.CREATED
JSONだけにしたい場合の対応
JSONだけにしたい場合は、
- settings.py中の
DEFAULT_PARSER_CLASSES
を変更する - 個別に設定する
の2つがあります。
今回は個別に設定して、テストコードにて確認してみます。
個別に設定する場合、ビューの parser_classes
にParserをタプルで指定します。
class AppleJsonOnlyViewSet(viewsets.ModelViewSet): queryset = AppleJsonOnly.objects.all() serializer_class = AppleJsonOnlySerializer parser_classes = (JSONParser, )
パスするテストコードは以下です。
@pytest.mark.django_db class TestAppleJsonOnlyViewSet: def test_formをPOSTする(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:applejsononly-list'), content_type='application/x-www-form-urlencoded', data=urlencode(data)) assert actual.status_code == HTTPStatus.UNSUPPORTED_MEDIA_TYPE, 'formからの送信は受け付けない' def test_JSONをPOSTする_content_typeを指定(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:applejsononly-list'), content_type='application/json', data=json.dumps(data)) assert actual.status_code == HTTPStatus.CREATED def test_JSONをPOSTする_formatを指定(self): client = APIClient() data = {'name': 'シナノゴールド'} actual = client.post(reverse('parser_classes_api:applejsononly-list'), format='json', data=data) assert actual.status_code == HTTPStatus.CREATED
ソースコード
GitHubに上げました。 parser_classes_api
が今回のアプリです。
https://github.com/thinkAmi-sandbox/drf_jquery-sample