Reputation: 9790
I'm using Django 2.x
and Django REST Framework
.
I have a model with contact
as a foreign key
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)
and serializer like
class AmountGivenSerializer(serializers.ModelSerializer):
mode_of_payment = serializers.PrimaryKeyRelatedField(queryset=ModeOfPayment.objects.all())
contact_detail = ContactSerializer(source='contact', read_only=True)
contact = serializers.PrimaryKeyRelatedField(queryset=Contact.objects.all())
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'contact_detail', 'amount', 'given_date', 'created'
)
contact
field is required while creating a new record. But I do not want contact
to be modified once it is created.
But when I send only amount
with PUT
method it says
{
"contact": [
"This field is required."
]
}
And when I use PATCH
method, it works fine but if passing some other value for contact
, it is updating contact
as well.
I want to make contact
field not-required
while updating existing record. And even if it is passed, use the earlier one instead of setting the new data.
Trial 2
I tried overriding the contact
field in the request to the previously stored value so that in case if changed contact
is passed or no contact
is passed, it will save earlier one.
So, in the viewset add the function
def update(self, request, *args, **kwargs):
obj = self.get_object()
request.data['contact'] = obj.contact_id
return super().update(request, *args, **kwargs)
But this is giving error as
This QueryDict instance is immutable
Upvotes: 3
Views: 2545
Reputation: 12990
If your viewset is a ModelViewSet
, you can overwrite the perform_update
hook (because ModelViewSet
inherits from GenericAPIView
(take a look at "Save and deletion hooks"). You can access the old contact using the serializer's instance
field:
class MyViewSet(viewsets.ModelViewSet):
# ... other stuff
def perform_update(self, serializer):
serializer.save(contact=serializer.instance.contact)
So you will have to provide a contact, but no matter which contact you provide, it will always use the old saved contact when updating.
Upvotes: 2
Reputation: 7108
Use __init__
method of serializer to make it read when object is being updated:
class AmountGivenSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
"""If object is being updated don't allow contact to be changed."""
super().__init__(*args, **kwargs)
if self.instance is not None:
self.fields.get('parent').read_only = True
# self.fields.pop('parent') # or remove the field
mode_of_payment = serializers.PrimaryKeyRelatedField(queryset=ModeOfPayment.objects.all())
contact_detail = ContactSerializer(source='contact', read_only=True)
contact = serializers.PrimaryKeyRelatedField(queryset=Contact.objects.all())
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'contact_detail', 'amount', 'given_date', 'created'
)
Using self.context['view'].action
is not recommended as it will not work when using the serializer out of DRF, eg. in normal Django views. It's best to use self.instance
as it will work in every situation.
Upvotes: 2