Django Rest Framework устанавливает поле read_only только после создания записи - PullRequest
0 голосов
/ 20 октября 2018

Я использую Django 2.x и Django REST Framework.

У меня есть модель с contact в качестве внешнего ключа

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)

и сериализатором типа

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 поле обязательно для заполнения при создании новой записи.Но я не хочу, чтобы contact был изменен после его создания.

Но когда я отправляю только amount методом PUT, он говорит:

{
    "contact": [
        "This field is required."
    ]
}

И когда я используюМетод PATCH, он работает нормально, но если передать другое значение для contact, он также обновляет contact.

Я хочу сделать contact поле not-required при обновлениисуществующая запись.И даже если он пройден, используйте более раннюю версию вместо установки новых данных.

Trial 2

Я попытался переопределить поле contact взапрос к ранее сохраненному значению, чтобы в случае, если измененное значение contact было передано или не было передано contact, оно сохранит предыдущее значение.

Таким образом, в представление добавьте функцию

def update(self, request, *args, **kwargs):
    obj = self.get_object()
    request.data['contact'] = obj.contact_id
    return super().update(request, *args, **kwargs)

Но это дает ошибку как

This QueryDict instance is immutable

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Если ваш viewset имеет значение ModelViewSet, вы можете перезаписать хук perform_update (поскольку ModelViewSet наследуется от GenericAPIView (взгляните на «Хуки сохранения и удаления»).можно получить доступ к старому контакту, используя поле instance сериализатора:

class MyViewSet(viewsets.ModelViewSet):
    # ... other stuff

    def perform_update(self, serializer):
        serializer.save(contact=serializer.instance.contact)

Таким образом, вам нужно будет предоставить контакт, но независимо от того, какой контакт вы предоставляете, он всегда будет использоватьстарый сохраненный контакт при обновлении.

0 голосов
/ 20 октября 2018

Используйте __init__ метод сериализатора, чтобы он читал при обновлении объекта:

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'
        )

Использование self.context['view'].action не рекомендуется, так как не будет работать при использовании сериализатора из DRF, например,в нормальных представлениях Джанго.Лучше использовать self.instance, так как он будет работать в любой ситуации.

...