📂

Django Ninjaでfileアップロード

2024/04/07に公開

今回はDjnago Ninjaでfileアップロードの仕組みを作っていきます。

Django Ninjaの公式ドキュメントはこちらです。
https://django-ninja.dev/guides/input/file-params/

1 models.pyの設定

model.pyにfileアップロード用のフィールドを用意します。
コードは以下の通りです(追加分だけ表示)。

backend/accounts/models.py
  file_name = models.CharField(_('file name'), blank = True, null = True)
  profile_image = models.FileField(upload_to='file/%Y%m%d', blank = True, null = True)

file_nameフィールドには、アップロードしたファイルに任意のファイル名を設定できるようにしています(なくてもいいです)。
profile_imageにはファイルアップロード先を指定しています。今回は、settings.pyのmediaに設定している保存先('/media/)の先をupload_toに設定しています。
設定したのは、「/media/file/%Y%m%d」となるURLでファイルを参照できるよにしており、日付ごとにファイルの保存先を変えています。

また、最初に設定しておくべきでしたが、ファイルの保存先の設定をurls.pyにも設定します。

backend/project/urls.py
from django.conf.urls.static import static # add
from django.conf import settings # add

~~

urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

2 admin.pyの設定

次に、アプリケーションのadmin.pyの設定をしていきます。
管理画面に設定したフィールドを表示させるための設定です。
Personal infoにfile_nameを、Permissionsにprofile_imageを追記しています(個人の設定に合わせて表示場所などは設定してください)。

backend/accounts/admin.py
  fieldsets = (
    (None, {"fields": ("email", "password")}),
    (_("Personal info"), {"fields": ("username", "first_name", "last_name", "birth_date", "file_name")}), #add
    (_("Permissions"), {
      "fields": (
        "is_active",
        "is_staff",
        "is_superuser",
        "groups",
        "user_permissions",
        "profile_image", # add
      )
    }),
    (_("Important dates"), {"fields": ("last_login", "date_joined")}),
  )

3 schemas.pyの設定

次にAPIで使用するschemaを設定していきます。
今回は事前に設定していたCustomUserOutに追記していきます。
基本はDjangoで設定したmodelをベースにschemaを生成するのですが、これに加えて、個別のschema(今回はfile_name)を追加しています。
【参考記事】
https://django-ninja.dev/guides/response/django-pydantic/

backend/accounts/schemas.py
class CustomUserOut(ModelSchema):
  file_name: str # add
  
  class Meta:
    model = CustomUser
    fields = ['uuid', 'username', 'email', 'first_name', 'last_name', 'profile_image'] # add

4 ファイルアップロード用のAPIを設定

それではここまで書いた設定を使用し、実際にファイルアップロードができるように設定をしていきます。

新規登録時

新規登録時のにprofile_imageも一緒にアップロードできるようにします。

backend/accounts/api.py
@router.post('/create', response = CustomUserOut, auth = None)
def create_user(request, payload: Form[CreateCustomUser], file: File[UploadedFile]):
  if CustomUser.objects.filter(email = payload.email).exists():
    return {
      'message': 'Email is already used. Please change email address.'
    }
  if CustomUser.objects.filter(username = payload.username).exists():
    return {
      'message': 'Username is already is used. Please other username set.'
    }
  
  user = CustomUser(
    uuid = str(uuid.uuid4()),# uuidはデフォルトでuuid型になるためstrに変換
    username = payload.username,
    email = payload.email,
    first_name = payload.first_name,
    last_name = payload.last_name,
  )
  user.file_name = file.name # add
  user.profile_image = file # add
  user.set_password(payload.password)
  user.save()
  return user

登録時用の更新をする場合

本来は更新用のAPIであれば、PUTメソッドを使用しますが、今回はPOSTメソッドを使用して、元々の投稿を上書きするようにしています。

backend/accounts/api.py
# profile_imageだけ更新するエンドポイント
@router.post('/author/profile_image/{uuid}', response = CustomUserOut)
def update_profile_image(
  request,
  uuid: str,
  file: UploadedFile = File(...)
):
  author = get_object_or_404(CustomUser, uuid = uuid)
  print(file)
  if file:
    author.profile_image = file
    author.file_name = file.name
    author.save()
  return author

# accounts情報と一緒にfileの更新もする場合
@router.post('/author/update/{uuid}', response = CustomUserOut)
def update_author_info(
  request,
  uuid: str,
  payload: UpdateUser,
  file: UploadedFile = File(...)
):
  author = get_object_or_404(CustomUser, uuid = uuid)
  if payload:
    for attr, value in payload.dict().items():
      setattr(author, attr, value)
  if file:
    author.file_name = file.name
    author.profile_image = file
  author.save()
  return author

以上で、Django Ninjaを使用してfileアップロードをすることができるようになります。
これを応用すれば、通常の投稿についても同じようにファイルアップロードを設定することができます。

Discussion