Reputation: 6523
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
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
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