[メモ] Django RESTful API の POST で画像データを Upload する


本シリーズのトップページ
https://qiita.com/robozushi10/items/4559a281d0319eb62c6c

はじめに

CI による自動テストの中で、テスト結果の証拠としてキャプチャした画像(PNG)を
REST POST を使って WEBアプリに画像を Upload する機構を作成したときの抜粋である.

ファイル構成

ここでは、次のようなファイル構成で記述している.

home
|-- foo
    |-- mysite ................... Django プロジェクト
        |-- myapp ................ Django アプリ
        |   |-- models.py
        |   略
        |-- apiv1 ................ DRF
        |   |-- serializers.py
        |   |-- views.py
        |   |-- urls.py
        略
        |-- config
            |-- settings.py
            |-- urls.py
            |-- wsgi.py
            略

myapp/models.py

次の点に留意して、モデル「XXXX」を実装する.

  • imgfile = models.ImageField(upload_to=メソッド) を定義する. (下記💥)

  • 上記 upload_to で指定されたメソッドを XXXX クラス内で実装する (下記🛑)

  • フィールド「imgfile」(下記💥) に設定された値が、
    get_upload_path関数(下記🛑) の引数 filename に設定されている.

  • get_upload_path関数内(下記🛑)で「XXXX」クラスの
    「restkey」「scenario」「testcase」を組み合わせて Upload 先のパスを組み立てている.

class XXXX(models.Model):
  """ テスト結果保持 TBL """
  def 🛑get_upload_path(self, filename):
    k = self.restkey
    s = self.scenario if self.scenario else 'scenario'
    t = self.testcase if self.testcase else 'testcase'
    return '/'.join(['./images', k, s, t, filename])
  restkey   = models.CharField(verbose_name='RESTful APIキー', max_length=64)
  scenario  = models.CharField(verbose_name='シナリオ名', null=True, blank=True, max_length=40)
  testcase  = models.CharField(verbose_name='テストケース名', null=True, blank=True, max_length=64)
💥imgfile   = models.ImageField(upload_to=🛑get_upload_path, null=True, blank=True)

apiv1/serializers.py

上記モデル「XXXX」に対する DRF シリアライザである.

「XXXX」クラスの「imgfile」のみ、別途serializers.ImageField()を使って定義する.(下記💚)

from rest_framework import serializers
from XXXX.models import *

class XXXXSerializer(serializers.ModelSerializer):
  """ テスト結果保持 TBL """
 imgfile = serializers.ImageField(use_url=True)  💚
  class Meta:
    model  = XXXX
    fields = '__all__'  

参考にした記事

内容 URL
REST によるファイルのアップロード方法 https://stackoverflow.com/questions/45559471/django-rest-framework-upload-image-api

 

config/urls.py

ルーティングを定義する.

エンドポイントとして「api/v1/」が指定された場合は、
後述のapi/v1/urls.py でルーティングさせる.

from django.contrib import admin
from django.urls import path
from django.urls import include
from django.conf.urls import url, include
from django.views.generic import TemplateView
from django.views.generic import RedirectView
from apiv2 import views

urlpatterns = [
  path('api/v1/', include('apiv1.urls')),
]

apiv1/urls.py

DRF に関するルーティングを定義する.
下記🏷️と📛の詳細については後述している.

from django.urls import path, include
from rest_framework import routers
from . import views

urlpatterns = [
  path('', include(router.urls)),
  path('myapp/XXXX/',      🏷️views.XXXXListCreateAPIView.as_view()),
  path('myapp/XXXX/<pk>/', 📛views.XXXXRetrieveUpdateDestroyAPIView.as_view()),
]

apiv1/views.py

前述の 🏷️ と 📛 のクラスの実装である.
次の 🏷️ と 📛 のクラスに対して、それぞれ次の 1行以外を追加した以外は特殊な記述は無い (はず)

parser_classes = (MultiPartParser, FormParser) #! 🎰引用

from rest_framework import generics
from rest_framework import status
from rest_framework import views  #! 📘引用
from django.shortcuts import get_object_or_404  #! 📘引用
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from rest_framework.parsers import MultiPartParser, FormParser #! 🎰引用
from rest_framework.generics import ListAPIView 

from myapp.models import *
from .serializers import *

class XXXXListCreateAPIView(views.APIView):🏷️
  """ XXXX の取得(一覧)・登録 API クラス """
  parser_classes = (MultiPartParser, FormParser) #! 🎰引用

  def get(self, request, *args, **kwargs):
    l = XXXX.objects.all()
    ser = XXXXSerializer(instance=l, many=True)
    return Response(ser.data, status.HTTP_200_OK)

  def post(self, request, *args, **kwargs):
    ser = XXXXSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    ser.save()
    return Response(ser.data, status.HTTP_201_CREATED)


class XXXXRetrieveUpdateDestroyAPIView(views.APIView):
  """ XXXX の取得(詳細)・更新・一部更新・削除 API クラス """
  parser_classes = (MultiPartParser, FormParser) #! 🎰引用

  def get(self, request, pk, *args, **kwargs):
    oc = get_object_or_404(XXXX, pk=pk)
    ser = XXXXSerializer(instance=oc)
    return Response(ser.data, status.HTTP_200_OK)

  def put(self, request, pk, *args, **kwargs):
    oc = get_object_or_404(XXXX, pk=pk)
    ser = XXXXSerializer(instance=oc, data=request.data)
    ser.is_valid(raise_exception=True)
    ser.save()
    return Response(ser.data, status.HTTP_200_OK)

  def patch(self, request, pk, *args, **kwargs):
    oc = get_object_or_404(XXXX, pk=pk)
    ser = XXXXSerializer(instance=oc, data=request.data, partial=True)
    ser.is_valid(raise_exception=True)
    ser.save()
    return Response(ser.data, status.HTTP_200_OK)

  def delete(self, request, pk, *args, **kwargs):
    oc = get_object_or_404(XXXX, pk=pk)
    ser.delete()
    return Response(status.HTTP_204_NO_CONTENT)

参考にした記事

内容 URL 備考
APIView のために必要 現場で使える Django REST Framework の教科書 (Django の教科書シリーズ) 上記📘
REST によるファイルのアップロード方法 https://stackoverflow.com/questions/45559471/django-rest-framework-upload-image-api 上記🎰

 

以上.