Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A Test to Support Both JSON and File Multipart Uploads in DRF

I would like to write a test for my DRF app that posts both json and a file using multipart.

This is what I have tried so far but collection_items (in the create method) is blank. Do I need to modify my view to make this work correctly, or am I doing something incorrectly within my test case below?

My Test:

    image = Image.new('RGB', (100, 100))
    tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
    image.save(tmp_file)

    files = {"collection_items": [{"image": tmp_file}]}
    payload = json.dumps({
        "title": "Test Collection",
    })

    self.api_factory.credentials(Authorization='Bearer ' + self.token)
    response = self.api_factory.post(url, data=payload, files=files, format='multipart')

This is the model:

class Collection(models.Model):

    title = models.CharField(max_length=60)
    collection_items = models.ManyToManyField('collection.Item')


class Item(models.Model):
    image = models.ImageField(upload_to="/",null=True, blank=True)

Serializers:

class ItemCollectionDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ('id', 'image')
        read_only_fields = ('image',)


class CollectionListSerializer(serializers.ModelSerializer):

    url = serializers.HyperlinkedIdentityField(view_name='col_detail')
    collection_items = ItemCollectionDetailSerializer(many=True, required=True)

    class Meta:
        model = Collection
        fields = ('url', 'id', 'collection_items')

    def create(self, validated_data):

        item_data = validated_data.pop('collection_items')

        print(item_data)  # <----- **EMPTY HERE???**

        etc ....edited for brevity

So print(item_data) is empty [], why? How to I resolve this?

This is my entire view: below, do I need to do something here?

class CollectionListView(generics.ListCreateAPIView):

    queryset = Collection.objects.all()
    serializer_class = CollectionListSerializer

I am using Django Rest Framework 3.x, Django 1.8.x and Python 3.4.x.

Update

I have tried below but still no joy! collection_items is empty in my create. This either has to do with the fact it's a nested object or something has to happen in my view.

    stream = BytesIO()
    image = Image.new('RGB', (100, 100))
    image.save(stream, format='jpeg')
    uploaded_file = SimpleUploadedFile("temp.jpeg", stream.getvalue())

    payload = {
        "title": "Test Collection",
        "collection_items": [{"image": uploaded_file}],
    }

    self.api_factory.credentials(Authorization='Bearer ' + self.test_access.token)
    response = self.api_factory.post(url, data=payload, format='multipart')

Update 2

If I change my payload to use json.dumps it seems to now see the file but of course this cannot work!

payload = json.dumps({
            "title": "Test Collection",
            "collection_items": [{"image": uploaded_file}],
        })

Error

<SimpleUploadedFile: temp.jpeg (text/plain)> is not JSON serializable

PS

I know the file is being uploaded because if I do the following in my serializer...

print(self.context.get("request").data['collection_items'])

I get

{'image': <SimpleUploadedFile: temp.jpeg (text/plain)>}
like image 574
Prometheus Avatar asked Sep 03 '15 15:09

Prometheus


1 Answers

Using the multipart parser you can just pass the file handler in the post arguments (see this). In your code you are submitting a json-encoded part as the data payload and the file part in a files argument, and I don't think it can work that way.

Try this code:

from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import SimpleUploadedFile

stream = BytesIO()
image = Image.new('RGB', (100, 100))
image.save(stream, format='jpeg')

uploaded_file = SimpleUploadedFile("file.jpg", stream.getvalue(), content_type="image/jpg")
payload = {
    "title": "Test collection",
    "collection_items": [{"image": uf}],
}
self.api_factory.credentials(Authorization='Bearer ' + self.token)
self.api_factory.post(url, data=payload, format='multipart')
...

I'm not entirely sure the nested serialization works, but at least the file upload should work.

like image 82
dukebody Avatar answered Oct 21 '22 09:10

dukebody