Mary
Mary

Reputation: 41

DRF Upload multiple file

I'm using DRF to create an user with multiple attachments. When you create an user you have to upload one or more files. When I make an update of an user which you load a new file (no other modified field) the response back the old instance.

I solved by forcing the '_prefetched_objects_cache' attribute in the serializer before returning the instance.

setattr(instance, '_prefetched_objects_cache', True)

Is it correct? You have other solutions about it? thanks

There is my code

in models.py

class User(models.Model):
   #field of user model

class Attachment(models.Model):
    class Meta:
        db_table = 'attachment'

    path = models.FileField()
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='attachments')
    dt_created = models.DateTimeField(auto_now_add=True, verbose_name='Created')

in serializer.py

class AttachmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Attachment
        fields = '__all__'

class UserSerializer(serializers.ModelSerializer):
  attachments = AttachmentSerializer(many=True, read_only=True)


  def create(self, validated_data):

      user = User.objects.create(**validated_data)

      for file_item in self.initial_data.getlist('attachments'):
         c = Attachment(path=file_item, user=user)
         c.save()
      return user

  def update(self, instance, validated_data):
      for item in validated_data:
          if User._meta.get_field(item):
              setattr(instance, item, validated_data[item])

       c = Attachment(path=self.context['request'].FILES['attachments'], user=instance)
       c.save()

       instance.save()
       setattr(instance, '_prefetched_objects_cache', True)
       return instance

in test.py

io = StringIO.StringIO()
io.write('foo')
file = InMemoryUploadedFile(io, None, 'foo.txt', 'text', io.len, None)
file.seek(0)
self.user['attachments'] = [file, file]
data = self.user
response = self.client.post(url, data, format='multipart')
file = InMemoryUploadedFile(io, None, 'foo2.txt', 'text', io.len, None)
file.seek(0)
#url = url of user detail for update
local_user['attachments'].extend(response.data['attachments'])
local_user['attachments'].append(file)
data = local_user
response = self.client.put(path=url, data=data, format='multipart')

Upvotes: 4

Views: 5158

Answers (1)

ihoryam
ihoryam

Reputation: 2521

In case you use form for posting to DRF, you could use FormParser along with MultiPartParser if you're posting your data using forms

ex.

...
from rest_framework.parsers import MultiPartParser, FormParser
...

class UserView(APIView):
    parser_classes = (FormParser, MultiPartParser)

    def post(self, request):
        ...

Files will be available under request.data or request.FILES as InMemoryUploadedFile instance. You can proceed working with those in serializer or whatever fits better for you view logic.

Don't forget to set enctype to multipart/form-data in your form, otherwise files won't get parsed properly ex.

<form action="..." method="POST" enctype="multipart/form-data">
  <input type="file" name="file-1" />
  <input type="file" name="file-2" />
</form>

Upvotes: 1

Related Questions