Search code examples
djangoputdjango-ninja

Django Ninja Update a FileField


I am trying to create a simple restful API using Django-ninja.

Here is the post method to add a new record:

@router.post('/add',
             response={201: WordSchema, 401: DefaultSchema, 404: DefaultSchema},
             tags=["Dictionary"])
def add_word(request, lang1: str, lang2: str, word_class: int = None,
             description: str = None, example: str = None,
             sound: UploadedFile = None, image: UploadedFile = None):
    if not request.auth.is_superuser:
        return 401, {"detail": "Unauthorized"}
    try:

        if word_class is not None:
            word_class = WordClass.objects.get(pk=word_class)

        word = Word.objects.create(
            created_by=request.auth, lang1=lang1, lang2=lang2,
            word_class=word_class,
            description=description,
            example=example,
            sound=sound,
            image=image
        )
        return 201, word
    except WordClass.DoesNotExist:
        return 404, {"detail": "WordClass Does not exist"}

This endo point works without a problem. The next step is to create a put endpoint.

@router.put('/put',
            response={200: DefaultSchema, 201: WordSchema,
                      401: DefaultSchema, 404: DefaultSchema,
                      409: DefaultSchema},
            tags=["Dictionary"])
def put_word(request, item_id: int, lang1: str, lang2: str,
             description: str = None, example: str = None,
             word_class: int = None, sound: UploadedFile = None,
             image: UploadedFile = None):
    if not request.auth.is_superuser:
        return 401, {"detail": "Unauthorized"}

    try:
        if word_class is not None:
            word_class = WordClass.objects.get(pk=word_class)

        word = Word.objects.get(pk=item_id)
        word.lang1 = lang1
        word.lang2 = lang2
        word.word_class = word_class
        word.description = description
        word.example = example
        word.sound = sound
        word.image = image
        word.save()
        return 200, {"detail": "Record updated"}
    except WordClass.DoesNotExist:
        return 404, {"detail": "WordClass Does not exist"}
    except Word.DoesNotExist:
        word = Word.objects.create(created_by=request.auth,
                                   lang1=lang1, lang2=lang2,
                                   word_class=word_class,
                                   sound=sound, image=image)
        return 201, word

Unfortunately, the put endpoint does not work as intended. There is no error raised but the file fields are neither sending the files nor getting a value (The value is None).

I used the swagger and uploaded the file using it.

But file upload does not work.

Update

If I change the HTTP Method to POST it works. But it does not work with PUT method.


Solution

  • Found where the problem lies. It was not on my side but on the django-ninja's side. The framework does not support file upload with PUT and PATCH methods.

    However, there is a solution:

    https://github.com/vitalik/django-ninja/pull/719, https://pypi.org/project/ninja-put-patch-file-upload-middleware/

    Solution:

    First, install the middleware:

    pip install ninja-put-patch-file-upload-middleware
    

    Add it on settings file:

    # settings.py
    
    MIDDLEWARE = [
        ...
        "ninja_put_patch_file_upload_middleware.middlewares.process_put_patch",
    ]
    

    Now the PUT and PATCH methods will accept file uploads.

    Unittest

    Please be aware that this solution lacks the ability to unit test the code using Django's builtin TestCase class.