jvc26
jvc26

Reputation: 6523

Django rest framework save object issues

Analysis

In summary, the below should be expected to work, and a solution is presented below. I've left the question in case anyone else has got lost in this.

Original Question

In the Django M2M tutorial, we have pizzas and toppings. As part of my application I have toppings which are M2M fields, and an endpoint to add new toppings. However, I only want to add the topping if it doesn't already exist, otherwise I just want to return the existing one. This is to avoid a nested-M2M relation between Pizzas and Toppings causing issues, instead I have two endpoints, one for pizzas, one for toppings, and then the toppings are added post_save in the view.

However, the following appears not to work:

class ToppingSerializer(serializers.ModelSerializer):
    another_model = serializers.PrimaryKeyRelatedField()

    class Meta:
        model = Topping
        fields = ('id', 'another_model', 'name')

    def save_object(self, obj, **kwargs):
        topping = Topping.objects.get_or_create(another_model=obj.another_model,
                                                name=obj.name)
        return topping

With a view code as below:

    [snip - View code below]
    serializer_class = ToppingSerializer

    serializer = self.get_serializer(data=request.DATA, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(status=status.HTTP_201_CREATED)
    [snip]

Upvotes: 2

Views: 3591

Answers (2)

jvc26
jvc26

Reputation: 6523

In summary, the above should be expected to work, as the save() and save_object() calls are called on the self.object in question, and do not return anything. Therefore it should be expected that the above won't work. Instead, iterating the list of self.object in an overridden save() method, and replacing the pre-save self.object list with that, does the trick.

An example would follow below:

def save(self, **kwargs):
    """
    Unintelligent save via get_or_create(). This does not handle logic
    for adding/removing toppings from parent (read pizza) models. It 
    merely puts new toppings into the database.
    """
    # Clear cached _data, which may be invalidated by `save()`
    self._data = None

    if isinstance(self.object, list):
        saved_mappings = [Topping.objects.get_or_create(another_model=item.another_model, name=item.name)[0] for item in self.object]
    else:
        saved_mappings = Topping.objects.get_or_create(another_model=self.object.another_model, name=self.object.name)

    self.object = saved_mappings

    return self.object

Upvotes: 0

AlvaroAV
AlvaroAV

Reputation: 10563

I think you need to change your sentence and add ".all()" at the end of "serializer.objects" :

for topping in ToppingSerializer.objects.all():
    print topping.id

and be careful with the capitals letters. To get all the object of a class you need to call that class. If you define the class like ToppingSerializer, to get all the objects of that class you have to call "ToppingSerializer.objects.all()" and you will get a list of objects as return.

Upvotes: 1

Related Questions