Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading a file with Django REST framework ListCreateAPIView

Using Django REST framework's generic ListCreateAPIView, I've created an end point that I think should be able to upload photos via POST requests. I am modeling my code on this tutorial.

So far I have attempted to POST a file to this endpoint using both Android and curl, and observed the same behavior: a new record is created, but the file is not attached. Because the file is a required field, the server then returns a 500 error.

This question looks similar, but he's not using REST framework's generic views and I'm not sure why... I'd like to take advantage of them where applicable.

Here is my Django view:

class PhotoList(generics.ListCreateAPIView):
    model = Photo
    serializer_class = PhotoSerializer
    permission_classes = [
        permissions.AllowAny
    ]

...my model:

def get_unique_image_file_path(instance=None, filename='dummy.jpg'):
    """
    function to determine where to save images.  assigns a uuid (random string) to each and places it
    in the images subdirectory below media.  by default, we assume the file is a .jpg
    """
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext)
    # TODO: 'images' is hard coded
    return os.path.join('images', filename)

class Photo(models.Model):
    post = models.ForeignKey(Post, related_name='photos', null=True, blank=True)
    image = models.ImageField(upload_to=get_unique_image_file_path)

    def get_image_abs_path(self):
        return os.path.join(settings.MEDIA_ROOT, self.image.name)

... and my serializer:

class PhotoSerializer(serializers.ModelSerializer):
    image = serializers.Field('image.url')

class Meta:
    model = Photo

Reproducing the bug

To generate the behavior, I POST to the server using the following curl command (exactly the same thing happens with my Android client code):

curl --form image=@test_image.jpg http://localhost:8000/rest_tutorial/photos

Looking into generics.ListCreateAPIView, the create() method looks like this:

# Copied from rest_framework.mixins.CreateModelMixin 

def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.DATA, files=request.FILES)

    if serializer.is_valid():
        self.pre_save(serializer.object)
        self.object = serializer.save(force_insert=True)
        self.post_save(self.object, created=True)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED,
                        headers=headers)

    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

As I step through the above code in PyCharm's debugger, I can clearly see my file in the serializer's init_files field after serializer.get_serializer(). However, serializer's object field has a bunch of tracebacks but no references to my image file. Maybe something is wrong here?

After self.object = serializer.save(force_insert=True) the record is created with an empty image field, the file is not uploaded, and self.object.image.file just contains a traceback referring to a ValueError.

Any ideas? Thanks!

like image 918
the911s Avatar asked Apr 18 '14 20:04

the911s


People also ask

How do I import a CSV file into Django Rest Framework?

With all that done, you can go ahead and start your django server, navigate to the url you added which may look like this “http://127.0.0.1:8000/upload/”. Django Rest will display a page for you to upload your file, go ahead and upload a csv file using the sample data above and check your database to confirm it works.

Why use Django Rest Framework instead of Django?

Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django. It adopts implementations like class-based views, forms, model validator, QuerySet, and more.


1 Answers

I believe the issue is in your serializer class where you define the image model field as Field. The generic Field is read-only, so that may be the source of the problem. Try simply removing that from your serializer, as it isn't necessary:

class PhotoSerializer(serializers.ModelSerializer):

    class Meta:
        model = Photo

Hope this helps, let me know how you get along.

like image 190
Fiver Avatar answered Oct 28 '22 05:10

Fiver