2

I'm trying to create a new object using Django REST Framework's ModelViewSet and the ModelSerializer and associate an existing object to it's ForeignKey field. I have two models with a one-to-many relationship, basically a manufacturer and a product. The manufacturer already exists in the database and I'm trying to add a product individually and assign it's manufacturer at the same time. When I do, DRF is giving me this error:

"non_field_errors": [ "Invalid data. Expected a dictionary, but got Product." ]

Here are what my models look like (I've tried to take out the unneeded code to keep it small). When a product is created the manufacturer field is an integer representing the ID of it's related object. As you can see in my view, I query for the Manufacturer object, set it and then continue to let DRF create the object. If someone can please tell me where I'm going wrong, I'd greatly appreciate it!

models.py

class Manufacturer(models.Model):
    name = models.CharField(max_length=64)
    state = models.CharField(max_length=2)

class Product(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, related_name="products")
    name = models.CharField(max_length=64)
    sku = models.CharField(max_length=12)

views.py

class ProductViewSet(view sets.ModelViewSet):
    model = Product
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        # get logged in user's manufacturer
        return Product.objects.filter(manufacturer=manufacturer)

    def create(self, request, *args, **kwargs):
        if "manufacturer" in request.data:
            manufacturer = Manufacturer.objects.get(pk=request.data["manufacturer"])
            request.data["manufacturer"] = manufacturer

        return super(ProductViewSet, self).create(request, *args, **kwargs)

serializers.py

class ManufacturerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Manufacturer
        fields = ("id", "name", "state",)
        read_only_fields = ("id",)

class ProductSerializer(serializers.ModelSerializer):
    manufacturer = ManufacturerSerializer()

    class Meta:
        model = Product
        fields = ("id", "manufacturer", "name",)
        read_only_fields = ("id",)

1 Answer 1

3

you have to set manufacturer_id key.

change your serializer to:

class ProductSerializer(serializers.ModelSerializer):
    manufacturer = ManufacturerSerializer(read_only=True)
    manufacturer_id = serializers.IntegerField(write_only=True)

    class Meta:
        model = Product
        fields = ("id", "manufacturer", "name",)
        read_only_fields = ("id",)

change your create method to:

def create(self, request, *args, **kwargs):
    if "manufacturer" in request.data:
        request.data["manufacturer_id"] = request.data['manufacturer']

    return super(ProductViewSet, self).create(request, *args, **kwargs)
Sign up to request clarification or add additional context in comments.

2 Comments

This works for inserts and kind of works for updates. For example, I have a product "X" that belongs to manufacturer "A". I update "X" to have a new manufacturer "B". The object returned to me from the update method still shows "A" as the manufacturer but the database was actually updated to "B". If I refresh the page it has the correct value. I've gone through the DRF code and can't seem to understand why this is the case. Any thoughts?
Ok, I overrode the update method in my serializer and called instance.refresh_from_db() and that seems to have solved it. It feels kind of "hacky" but it works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.