1.15 Django REST Framework(DRF) 画像アップロード [Django]
Django Rest Frameworkで画像アップロードの実現サンプルとなります。
画像アップロードは、カスタムのUserModelに、imageという画像用列を追加します。
動作環境 pip list コマンド結果は下記
DRFサイトに記載があるfileuploadparser、MultiPartParserを使用します。
クライアント側から送信時はmedia_typeを「multipart/form-data」にしてくれとのこと
(https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser)
setting.py
カスタムのUser Model(画像保存の項目はimage)
model.py
とくだん説明はないです。
urls.py
クライアントからは、Formdataで、"image"というキーワードにイメージファイルを設定してる場合request.data.imageでアクセス出来る。
InMemoryFileUploadHandlerというオブジェクトになるらしいので、
どんなメソッドがあるかの確認はソースをみると理解出来ます。
Djaogo Document
保存するファイル名は、request.data['image'].nameに設定することで
親クラスのupdateを呼出すことで、Django側が良しなに処理してくれる。
(画像保存先のフォルダまで作成してくれるかはしらん)
ソース内のコメントアウトしているコードは、Djangoが保存してくれると知らずに、
自力で保存した処理を記念?に残している
view.py
これでクライアントから、PATCH or PUT メソッドで送信すればUserの更新が出来ました。
もちろん、他のDB項目であるusernameやfirst_nameも更新されました。
クライアントは、Next.jsでやったのですが、axiosを利用せず、
fetch関数で実行しましたが、沢山はまったけど、なかでもハマリポイントは下記だった。
Content-Typeを指定しないようにしよう
Next.jsの理解が進んだら、アップロードのクラアント側も掲載しようかな・・?
画像アップロードは、カスタムのUserModelに、imageという画像用列を追加します。
動作環境 pip list コマンド結果は下記
asgiref 3.5.2 Django 3.2.15 django-cors-headers 3.10.1 django-filter 22.1 djangorestframework 3.13.1 djangorestframework-jwt 1.11.0 importlib-metadata 4.12.0 Markdown 3.4.1 mysqlclient 2.1.1 Pillow 9.2.0 pip 22.2.2 PyJWT 1.7.1 pytz 2022.2.1 setuptools 57.5.0 sqlparse 0.4.2 uWSGI 2.0.20 wheel 0.37.1 zipp 3.8.1
DRFサイトに記載があるfileuploadparser、MultiPartParserを使用します。
クライアント側から送信時はmedia_typeを「multipart/form-data」にしてくれとのこと
(https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser)
setting.py
REST_FRAMEWORK = { # # Default Parser Class Setting # https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.MultiPartParser', ], } STATIC_URL = '/static/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/'
カスタムのUser Model(画像保存の項目はimage)
model.py
class User(AbstractBaseUser, PermissionsMixin): uuid = models.UUIDField(default=uuid_lib.uuid4, primary_key=True, editable=False) username_validator = UnicodeUsernameValidator() username = models.CharField(_("username"), max_length=50, validators=[username_validator], unique=True) first_name = models.CharField(_("first_name"), max_length=50, ) last_name = models.CharField(_("last_name"), max_length=50, ) nick_name = models.CharField(_("nick_name"), max_length=50, ) email = models.EmailField(_("email_address"), unique=True) is_staff = models.BooleanField(_("staff status"), default=False) is_active = models.BooleanField(_("active"), default=True) authorization = models.IntegerField(_("authorization"), default=0 ) date_joined = models.DateTimeField(_("date joined"), default=timezone.now) image = models.ImageField(upload_to='images/', blank=True) objects = UserManager() USERNAME_FIELD = "username" EMAIL_FIELD = "email" REQUIRED_FIELDS = ['email',] class Meta: verbose_name = _("user") verbose_name_plural = _("users") def clean(self): super().clean() self.email = self.__class__.objects.normalize_email(self.email) def email_user(self, subject, message, from_email=None, **kwargs): send_mail(subject, message, from_email, [self.email], **kwargs)
とくだん説明はないです。
urls.py
from users.views import ( UserViewSet, GroupViewSet, ) router = routers.DefaultRouter() router.register(r'users', UserViewSet) urlpatterns = format_suffix_patterns(urlpatterns) urlpatterns += router.urls
クライアントからは、Formdataで、"image"というキーワードにイメージファイルを設定してる場合request.data.imageでアクセス出来る。
InMemoryFileUploadHandlerというオブジェクトになるらしいので、
どんなメソッドがあるかの確認はソースをみると理解出来ます。
Djaogo Document
保存するファイル名は、request.data['image'].nameに設定することで
親クラスのupdateを呼出すことで、Django側が良しなに処理してくれる。
(画像保存先のフォルダまで作成してくれるかはしらん)
ソース内のコメントアウトしているコードは、Djangoが保存してくれると知らずに、
自力で保存した処理を記念?に残している
view.py
from rest_framework.parsers import FileUploadParser class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited. """ parser_class = (FileUploadParser, MultiPartParser) queryset = User.objects.all().order_by('username') serializer_class = UserSerializer # permission_classes = [permissions.IsAuthenticated] # # Filte機能設定 filter_backends = [DjangoFilterBackend] filterset_fields = ['uuid', 'username','email', 'first_name', 'last_name','nick_name'] def update(self, request, *args, **kwargs): logger.debug("Request Data") print(request.data) image_file = "" if 'image' in request.data: image_file = "images/" + str(uuid_lib.uuid4()) + ".png" request.data['image'].name = image_file super(UserViewSet, self).update(request, *args, **kwargs) ''' if 'image' in request.data: logger.debug("Image File Write") # 保存してみる logger.debug(f'MEDIA_ROOT={settings.MEDIA_ROOT}') image_file = "images/" + str(uuid_lib.uuid4()) + ".png" image_fullpath = os.path.join(settings.MEDIA_ROOT, image_file) # reqeust.data['image']は、InMemoryFileUploadHandlerというオブジェクトらしい with open(os.path.join(settings.MEDIA_ROOT, image_file), 'wb') as f: for chunk_data in request.data['image'].chunks(): f.write(chunk_data) logger.debug(f'Image File Write Success file[{image_file}]') else: logger.debug("No Image File") ''' return Response({'success': 'Imported successfully'})
これでクライアントから、PATCH or PUT メソッドで送信すればUserの更新が出来ました。
もちろん、他のDB項目であるusernameやfirst_nameも更新されました。
クライアントは、Next.jsでやったのですが、axiosを利用せず、
fetch関数で実行しましたが、沢山はまったけど、なかでもハマリポイントは下記だった。
Content-Typeを指定しないようにしよう
Next.jsの理解が進んだら、アップロードのクラアント側も掲載しようかな・・?
コメント 0