Reputation: 215
I'm trying to use a ModelSerializer for a Model that has a Many to Many field.
This is part of my model:
class BaseSearchService(models.Model):
advertisements = models.ManyToManyField(Advertisement, null=False,
blank=False)
And this is the serializer
class SearchTransportSickPersonSerializer(serializers.ModelSerializer):
person = TransportPersonComponentSerializer()
owner = serializers.ReadOnlyField(source='owner.username')
advertisements = serializers.ReadOnlyField()
class Meta:
model = SearchTransportSickPerson
fields = ('name', 'description',
'date_service_full_day', 'date_service', 'origin_lat',
'origin_long', 'destinity_lat', 'destinity_long', 'multiple_stops',
'person', 'owner', 'advertisements')
def create(self, validated_data):
user_data = validated_data.pop('owner')
advs = validated_data.pop('advertisements')
user = User.objects.get(email=user_data.email)
person_data = self.validated_data.pop('person')
person = TransportPersonComponent.objects.create(**person_data)
sickPerson = SearchTransportSickPerson.objects.create(person=person,
owner=user, **self.validated_data)
for adv in advs:
a = Advertisement.objects.get(id=adv.id)
sickPerson.advertisements.add(a)
sickPerson.save()
return sickPerson
I have writen a unit test that replicates the error:
def test_serializer_2(self):
transportSickPerson = SearchTransportSickPerson(name="name",
description="desc", date_service=date_serv, date_service_full_day=
True, person=self.component, owner=self.user)
transportSickPerson.save()
transportSickPerson.advertisements.add(advertise)
serializer = SearchTransportSickPersonSerializer(transportSickPerson)
content = JSONRenderer().render(serializer.data)
When I try to render the serializer with the JSON Renderer, the following error gets thrown:
TypeError: .ManyRelatedManager object at 0x7f648017bfd0> is not JSON serializable
And when i print the serializer data, the dict type has the advertisements field as the ManyRelatedManager representation.
How can i solve this error? Is there any way to tell the serializer how to render that specific field?
Thank you for any help that you can provide.
Upvotes: 7
Views: 13447
Reputation: 10564
TL;DR: The answer is class.property.all()
.
The problem here lies in the fact that a ManyRelatedManager
is NOT the actual list of related objects, but a Django class from which you have to call all()
(or filter()
or get()
) in order to get to your data. Trying to serialize the related manager makes no sense.
Look at this example:
class SomeClass(models.Model):
friends = models.ManyToManyField(User)
if I have an instance of SomeClass
called some_class
, calling some_class.friends
will return:
<RelatedManager>
while some_class.friends.all()
will return
[<User>, <User>, ecc]
Side note:
you should not use null=False
on ManyToManyField
: it is useless. As you already know, when you create a new BaseSearchService
your advertisements
field is empty (thus, demonstrating that null=False serves no purpose). This is because, at database level, M2M fields are not a column in your BaseSearchService
table but a table in itself.
Every time you call .add()
(or any other manager methods like .remove()
) a call to the database is made, independently from the .save()
method of the model instance on which there is the M2M field. In other words, no need to call sickPerson.save()
also.
Upvotes: 9