How to POST a nested data and list of image
Asked Answered
S

3

9

There are two models: Product and Picture. Each Product can have several Pictures. I have questions when I want to create a Product using POST. How to POST a nested object containing a ImageField list?

The Product model is:

class Product(models.Model):
    product_id = models.AutoField(primary_key=True)
    product_name = models.CharField(max_length=50)
    description = models.TextField(blank=True)

The Picture model is:

class Picture(models.Model):
    product = models.ForeignKey(Product, related_name='pictures')
    path = models.ImageField(null=False, upload_to='product_pic')
    description = models.CharField(max_length=255, null=True, blank=True)
    main = models.BooleanField()

I write the serializer.py as follow:

class PictureSerializer(serializers.ModelSerializer):
    class Meta:
        model = Picture
        fields = ('path', 'description', 'main')

class ProductSerializer(serializers.ModelSerializer): 
    pictures = PictureSerializer(many=True, required=False)

    class Meta:
        model = Product
        fields = ('product_id', 'product_name', 'pictures', 'description')

The view that I am using is:

class ProductEnum(generics.ListCreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    permission_classes = (IsAuthenticated, )

    def post(self, request, format=None):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

I am registering it in the urls as:

url(r'^api/products/$', views.ProductEnum.as_view()),

The questions are:

  • How could I test this POST api as django-rest-framework tells me that "Lists are not currently supported in HTML input"
  • How to use JSON to POST a Product resources with multiple Pictures. OR I must use multipart parser.
  • How to write cURL command?
Strander answered 22/1, 2015 at 16:0 Comment(1)
did you manage to get it right?Wurster
Q
2

You have to use multipart parser, as soon as you need to send binary data you basically only have to choices:

  1. weaken your REST approach and make an exception -> json cannot hold binary data
  2. encode every binary file with base64, keep json approach but you need to do de/encoding additionally for every request (not natively supported)

An approach that can be seen quite often is to create an (non rest) view to upload single/multiple files which create File or Document objects (returning an id on upload). Then you can use these id(s) which another request, in your case creating/updating your Product.

In general there is no easy way to do that because json doesn't support binary data.

Quartermaster answered 28/9, 2017 at 11:44 Comment(0)
S
1

DRF makes this quite easy to do. You are close, you need to override ProductSerializer.create, something like this:

class ProductSerializer(serializers.ModelSerializer): 
    pictures = PictureSerializer(many=True, required=False)

    class Meta:
        model = Product
        fields = ('product_id', 'product_name', 'pictures', 'description')

    def create(self, validated_data):
        # Pop this from the validated_data first, since the serializer can't handle it.
        pictures = validated_data.pop('pictures')
        product = super().create(validated_data)
        # Now that we have a product to reference in the FKey, create the pictures.
        for picture in pictures:
            # `picture` has been run through the PictureSerialzer, so it's valid. 
            picture['product'] = product
            Picture.objects.create(**picture)
        return product

There's a full example in the docs, here: http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers

For your curl command, it's going to be something like:

curl -X POST http://localhost:8000/products/ -H 'ContentType: application/json' -d '{"pictures": [{"description": "first one"}, {"description": "second one"}], "product_name": "foobar"}'

Shellashellac answered 9/6, 2017 at 19:10 Comment(1)
hi there, I did as you do, but doesn't work. please helpMureil
A
0

You can use manage.py shell. Like this:

import requests
r = requests.post("http://localhost:8000/login/url", data={"username": "username", "password": "password"}
r.content (outputs token)
token="yourtoken"
r = requests.post("http://localhost:8000/your/url", data={your POST data in json}, headers={"Authorization": "Token %s" % token})
Apatite answered 4/7, 2015 at 15:40 Comment(1)
You didn't answer the poster's golden question - How to use JSON to POST a Product resources with multiple Pictures. OR I must use multipart parser.Wurster

© 2022 - 2024 — McMap. All rights reserved.