I'm using Django 2.x
and Django REST Framework
.
I have two models like
class Contact(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.PROTECT)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True, null=True)
modified = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
class AmountGiven(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
amount = models.FloatField(help_text='Amount given to the contact')
given_date = models.DateField(default=timezone.now)
created = models.DateTimeField(auto_now=True)
the serializer.py the file has serializers defined as
class ContactSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contact
fields = ('id', 'first_name', 'last_name', 'created', 'modified')
class AmountGivenSerializer(serializers.ModelSerializer):
contact = ContactSerializer()
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'amount', 'given_date', 'created'
)
views.py
class AmountGivenViewSet(viewsets.ModelViewSet):
serializer_class = AmountGivenSerializer
def perform_create(self, serializer):
save_data = {}
contact_pk = self.request.data.get('contact', None)
if not contact_pk:
raise ValidationError({'contact': ['Contact is required']})
contact = Contact.objects.filter(
user=self.request.user,
pk=contact_pk
).first()
if not contact:
raise ValidationError({'contact': ['Contact does not exists']})
save_data['contact'] = contact
serializer.save(**save_data)
But when I add a new record to AmountGiven model and passing contact
id in contact
field
it is giving error as
{"contact":{"non_field_errors":["Invalid data. Expected a dictionary, but got str."]}}
When I remove contact = ContactSerializer()
from AmountGivenSerializer, it works fine as expected but then in response as depth
is set to 1, the contact data contains only model fields and not other property fields defined.
I'm not a big fan of this request parsing pattern. From what I understand, you want to be able to see all the contact's details when you retrieve an AmountGiven
object and at the same time be able to create and update AmountGiven
by just providing the contact id.
So you can change your AmountGiven
serializer to have 2 fields for the contact
model field. Like this:
class AmountGivenSerializer(serializers.ModelSerializer):
contact_detail = ContactSerializer(source='contact', read_only=True)
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'contact_detail', 'amount', 'given_date', 'created'
)
Note that the contact_detail
field has a source
attribute.
Now the default functionality for create and update should work out of the box (validation and everything).
And when you retrieve an AmountGiven
object, you should get all the details for the contact in the contact_detail
field.
I missed that you need to check whether the Contact
belongs to a user (however, I don't see a user
field on your Contact
model, maybe you missed posting it). You can simplify that check:
class AmountGivenViewSet(viewsets.ModelViewSet):
serializer_class = AmountGivenSerializer
def perform_create(self, serializer):
contact = serializer.validated_data.get('contact')
if contact.user != self.request.user:
raise ValidationError({'contact': ['Not a valid contact']})
serializer.save()
Override the __init__()
method of AmountGivenSerializer
as
class AmountGivenSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(AmountGivenSerializer, self).__init__(*args, **kwargs)
if 'view' in self.context and self.context['view'].action != 'create':
self.fields.update({"contact": ContactSerializer()})
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'amount', 'given_date', 'created'
)
Description
The issue was the DRF expects a dict like object from contact
field since you are defined a nested serializer
. So, I removed the nested relationship dynamically with the help of overriding the __init__()
method
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With