【问题标题】:Django Rest Framework set a field read_only after record is createdDjango Rest Framework 在创建记录后设置一个字段 read_only
【发布时间】:2019-03-25 00:59:18
【问题描述】:

我正在使用Django 2.xDjango 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 在创建后被修改。

但是当我使用PUT 方法只发送amount 时,它会说

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

当我使用PATCH 方法时,它工作正常,但如果为contact 传递一些其他值,它也会更新contact

我想在更新现有记录时创建contact 字段not-required。而且就算通过了,也不要设置新的数据,而是使用之前的那个。

试用 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

【问题讨论】:

标签: django django-rest-framework


【解决方案1】:

使用序列化程序的__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 之外的序列化器时不起作用,例如。在普通的 Django 视图中。最好使用self.instance,因为它适用于任何情况。

【讨论】:

    【解决方案2】:

    如果您的视图集是ModelViewSet,您可以覆盖perform_update 挂钩(因为ModelViewSet 继承自GenericAPIView(请查看“保存和删除挂钩”)。您可以使用访问旧联系人序列化程序的instance 字段:

    class MyViewSet(viewsets.ModelViewSet):
        # ... other stuff
    
        def perform_update(self, serializer):
            serializer.save(contact=serializer.instance.contact)
    

    所以你必须提供一个联系人,但无论你提供哪个联系人,更新时都会使用旧保存的联系人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 1970-01-01
      • 2019-06-08
      相关资源
      最近更新 更多