0

I am trying to post multiple data into my DataBase Using Django Rest framework (DRF).

AttributeError at /apiv2/api/processorder/order/ Got AttributeError when attempting to get a value for field subcategory on serializer MyProcessOrderSerializer. The serializer field might be named incorrectly and not match any attribute or key on the list instance. Original exception text was: 'list' object has no attribute 'subcategory'.

models.py

class SubCategory(models.Model):

    category = models.ForeignKey(Category, related_name='subcategory', on_delete=models.CASCADE)
    name = models.CharField("Food Name", max_length=50, help_text="Name of The Food")    
    price = models.DecimalField("Food Price", max_digits=5, decimal_places=2)
    quantity = models.PositiveIntegerField("Qty.", help_text="Quantity of the food Item you want")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    def __str__(self):
        return f'{self.name}'

    class Meta:
        verbose_name = 'SubCategory'
        verbose_name_plural = 'SubCategories'

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    bike = models.ForeignKey(User, related_name='bike', on_delete=models.CASCADE, blank=True, null=True)
    package = models.ForeignKey(PackageType, related_name='package', on_delete=models.CASCADE, blank=True, null=True)
    total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0000.0)  
    qty = models.PositiveIntegerField(default=1)
    shipping_address = models.CharField("Delivery Address", max_length=150)
    paid = models.BooleanField(default=False)
    ordernote = models.TextField("Order Notes", null=True)
    shipped = models.BooleanField(default=False)
    complete = models.BooleanField(default=False) 
    received = models.BooleanField(default=False)
    refund_requested = models.BooleanField(default=False)
    refund_granted = models.BooleanField(default=False)
    ref_code = models.CharField(max_length=20, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


    class Meta:
        ordering = ('-created_at',)

    def __str__(self):
        return '{}'.format(self.id)


    def order(self):
        if not hasattr(self, '_order'):
            self._order = self.order.all()
        return self._order

    

    '''
    def get_total_cost(self):
        total_cost = sum(orders.get_cost() for orders in self.order.all())
        return total_cost

    '''


class ProcessOrder(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    order = models.ForeignKey(Order, related_name='order', on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField("Qty.", default=1, help_text="Quantity of the food Item you want")
    #category = models.ForeignKey(Category, related_name='category', on_delete=models.CASCADE)
    subcategory = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f'{self.order} -- {self.subcategory.name}'

serializers.py

class MyProcessOrderSerializer(serializers.ModelSerializer):

    #subcategory_name = serializers.RelatedField(source='subcategory.id', read_only=True)
    #subcategory_set = SubCategoryOrderSerializer(many=True)


    class Meta:
        model = ProcessOrder
        fields = ('quantity', 'subcategory', 'user')

        read_only_fields = ('user', ) 

    def create(self, validated_data):
        return ProcessOrder.objects.create(**validated_data)

view.pf

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def processorder_view(request):
    orderuser = User.objects.get(id=request.user.id)    
    serializer = MyProcessOrderSerializer(data=request.data, many=True)
   
    if serializer.is_valid():
        order = Order.objects.create(user=orderuser, ref_code=create_ref_code())
        order.save()
        processorder = serializer.save(order=order, user=orderuser)
        return Response(MyProcessOrderSerializer(processorder).data, status=status.HTTP_201_CREATED)
    else:        #return Response("Process Order Created Successfully")
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

I am now trying to make a POST such as this but getting the above error.

[{ "quantity": 16, "subcategory": 1 }, { "quantity": 14, "subcategory": 3 }

]

enter image description here

enter image description here

3 Answers3

0

You have to explicitly specify the relation in the serializer either with one of the built-in serializer:

Serializer relations

class MyProcessOrderSerializer(serializers.ModelSerializer):
    # StringRelatedField and PrimaryKeyRelatedField are some of the built in ones.
    subcategory = serializers.StringRelatedField(source='subcategory.id', read_only=True)
    user = serializer.PrimaryKeyRelatedField()

    class Meta:
        model = ProcessOrder
        fields = ('quantity', 'subcategory', 'user')

        read_only_fields = ('user', ) 

    def create(self, validated_data):
        # If you want to accect new subcategory object via the endpoint you need further action here, see the docs.
        return ProcessOrder.objects.create(**validated_data)

or by creating a custom subcategory serializer and then in serializer:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('id', 'email', 'password', 'first_name', 'last_name')
class SubCategorySerializer(serializers.ModelSerializer):
    class Meta:
      fields = # whatever fields youd'e like to include

class MyProcessOrderSerializer(serializers.ModelSerializer):
    # StringRelatedField is One of the built in ones.
    sybcategory = SubCategorySerializer()
    user = UserSerializer()

    class Meta:
        model = ProcessOrder
        fields = ('quantity', 'subcategory', 'user')

        read_only_fields = ('user', ) 

    def create(self, validated_data):
        # If you want to accect new subcategory object via the endpoint you need further action here, see the docs.
        return ProcessOrder.objects.create(**validated_data)

And, I noticed you have Foreign Key to User in process order and order itself,

You could delete the user field from the process order and access it via the order relation, for that you can specify the user field in the subcategory serializer and the subcategory serializer in the process order serializer as I showed above.

And if I understand the purpose of creating a process order model, the relation should be a One to One and not Foreign key, But maybe I'm not seeing the whole picture here.

  • On using First Method: I got this error: IntegrityError at /apiv2/api/processorder/order/ NOT NULL constraint failed: app_processorder.subcategory_id – Olaniyan Adewale Aug 06 '20 at 08:38
  • relationships are represented with the primary key by default so you should provide the subcategory id in the request –  Aug 06 '20 at 08:40
  • It is possible to pick my form value in view or how should I solve it. I was able to achieve this in django via formset which is very very easy. – Olaniyan Adewale Aug 06 '20 at 08:50
  • The major thing I want to achieve is to have multiple ProcessOrder which mean someone can book order for more than one thing and all the order will make a single order in order Model – Olaniyan Adewale Aug 06 '20 at 08:57
0

I think the problem apear in request.data in this line :

      serializer = MyProcessOrderSerializer(data=request.data, many=True)

You should loop throught your object list and pass just the object not the whole list .

exemple :

{ "list_":[{"quantity": 7 ,"subcategory": 3}, {"quantity": 7 ,"subcategory": 3}] }

To get the first object from the request :

   request.data.get('list_')[0]
  • how? I don't undertsand you – Olaniyan Adewale Aug 06 '20 at 08:56
  • try to make your request in the same format of exemple . after that replace serializer = MyProcessOrderSerializer(data=request.data.get('content')[0] , many=True) , it should save the first object , after that you can make a loop throught the lenth of your list . – ahmed barbouche Aug 06 '20 at 09:02
  • serializer = MyProcessOrderSerializer(data=request.data.get('content')[0] , many=True): 'list' object has no attribute 'get' – Olaniyan Adewale Aug 06 '20 at 09:10
  • i think you do not change correcltly format of json request . make it in the same format of this : { "list_":[{"quantity": 7 ,"subcategory": 3}, {"quantity": 7 ,"subcategory": 3}] } – ahmed barbouche Aug 06 '20 at 09:14
  • I found something in other way you can see it https://stackoverflow.com/questions/14666199/how-do-i-create-multiple-model-instances-with-django-rest-framework – ahmed barbouche Aug 06 '20 at 09:14
  • Where should put this to: request.data.get('list_')[0], because I am getting an error message – Olaniyan Adewale Aug 06 '20 at 18:19
  • 1
    Thanks.... The code is working. Even, I got to know that part has been working before now just giving the error message due to get request – Olaniyan Adewale Aug 06 '20 at 23:46
0

I was able to solve the problem with this code both in my views and serialize

views.py

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def processorder_view(request):
    orderuser = User.objects.get(id=request.user.id) 
    data = request.data
    
    order = Order.objects.create(user=orderuser, ref_code=create_ref_code())
    

    
    if isinstance(data, list):
        serializer = MyProcessOrderSerializer(data=request.data, many=True)
        
    else:
        serializer = MyProcessOrderSerializer(data=request.data)
        
    if serializer.is_valid():
        processorder = serializer.save(order=order, user=request.user)
        return Response(status=status.HTTP_201_CREATED)
        #return HttpResponse("Question created", status=201)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

serializers.py

class MyProcessOrderSerializer(serializers.ModelSerializer):

    #subcategory_name = serializers.RelatedField(source='subcategory.id', read_only=True)
    #subcategory_set = SubCategoryOrderSerializer(many=True)
    #subcategory = serializers.StringRelatedField(source='subcategory.id', read_only=True)



    class Meta:
        model = ProcessOrder
        fields = ('quantity', 'subcategory', 'user')

        read_only_fields = ('user', ) 

    def create(self, validated_data):
        return ProcessOrder.objects.create(**validated_data)

Then, I was able to send multiple data with this sample.

[
    {
        "subcategory": 1,
        "quantity": 12
    },
    {
        
        "subcategory": 3,
        "quantity": 12
    }
]