Django REST FrameworkのDEFAULT_PARSER_CLASSESの初期値について

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フォームからの送信も受け付けることを確認します。

以下のようなDjangoDjango 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)),
]

 

動作

フォームに入力します。

f:id:thinkAmi:20200615082905p:plain:w400

 

送信ボタンを押すと、Django REST FrameworkのThe Browsable APIの画面に遷移し、POSTが成功したことが分かります。 

f:id:thinkAmi:20200615083015p:plain:w400

 
テストコード的には、以下のコードがすべてパスします。

@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