Support for Uploads when using Django Form #30
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is a Work In Progress PR w/ preliminary source code to show what I am trying to achieve and would like a feedback before I move forward with this idea.
Problem
Django Forms work in a different way when it comes to uploads. The uploads must be sent in the second argument of the Form constructor. The
FILES
object is typically a dictionary where the keys match the field names. If you send the file object key in the first argument, the file is ignored from the form:However, the Apollo Upload specification handles uploads in a different way. Firstly, the
request.FILES
object becomes an ordered dictionary of values such as{ "0": MemoryUploadedFilehandler(...) }
. Secondly, as far as I understood from specs and the source code, the "map" field helps the server to merge the uploaded files into a single field (as array or just one object). This works great when dealing files right inside of GraphQL/Graphene mutations. However, the way Django Forms and this library handles files is not aligned.Solution
I have decided to play with this idea (when I started with this idea, I was not aware of graphene-file-upload library but this example code will work regardless) and came up with a solution. I have written the source code that works with Django's ModelForm and I tried to make sure that the way the mutation classes are implemented are similar in architecture to Graphene Django's mutations. I have copied some things from Graphene Django docs to scaffold my idea because it was not possible to properly extend Graphene Django's model to only add the necessary logic; however, I want to refactor it to extend instead of replace these classes.
In the source code below, I am doing two things:
First and foremost, I am registering Django Form's FileField (ImageField is derived from FileField; so, mapping one field is enough) class to accept
Upload
type. This is because, by default FileField accepts only Strings and we will get an error (__init__.py
line 11-13). I put this register in__init__.py
; so that, the types are mapped as soon as you import the view custom GraphQL view.Then, I created a custom class
DjangoUploadModelFormMutation
that works exactly likeDjangoModelFormMutation
but tries to extract fields that are instances ofFileField
and store it in an array. Before Django Form is created for validation, the stored file field names are used to extract files from the input object. The extracted values are then sent as a second argument to Django Form (mutation.py line 50-53). I had to rewrite that part because Graphene Django uses keyword arguments to pass form data but I have failed to find a way to pass files as keyword arguments as well. So, I had to overrideget_form
method instead of overridingget_form_kwargs
method.Usage
When doing it this way, the usage becomes much easier because the developer does not need to write anything other than change the class name:
Of course this usage would be different for non Model Form but I think it is possible to make it as seamless as possible for existing codebases; so that, devs can work with validated form data instead of mutation inputs.
Things to do
If you would like this integration to be part of the library, I am willing to help with this.