Django Rest框架中的只写,只读字段


69

我有这样的模型:

class ModelA(models.Model):
    name = models.CharField()


class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA)

序列化器:

class ASerializer(serializers.ModelSerializer):
    model_b_ids = serializers.CharField()
    class Meta:
        model = ModelA
        write_only_fields = ('model_b_ids',)

意见:

class AView(CreateModelMixin, GenericViewSet):

    def perform_create(self, serializer): 
        model_b_ids = parse_somehow(serializer.validated_data["model_b_ids"])
        #do something...

我遇到的问题是“ model_b_ids”的问题

用户应在发送帖子数据时提交它。

我在perform_create中使用它来链接到相关模型。

但是那不是ModelA中的“真实列”,因此当我尝试保存它时会引发异常。

我试图从validated_data中弹出它,但是又在无法从模型读取model_b_ids的地方再次出现错误。关于正确使用这种字段有什么想法吗?

Answers:


100

如果我错了,请纠正我,但我不认为Django rest框架具有Meta属性

write_only_fields

根据他们的文档,您设置了在extra_kwargs中只写字段

例如

class UserSerializer(ModelSerializer):
    """
    ``Serializer`` for ``User`` ..
    """

    class Meta:
        model = User
        fields = ('id', 'email', 'first_name', 'last_name' ,'security_question', 'security_question_answer', 'password', 'is_active', 'is_staff')
        read_only_fields = ('is_active', 'is_staff')
        extra_kwargs = {
            'security_question': {'write_only': True},
            'security_question_answer': {'write_only': True},
            'password': {'write_only': True}
        }

57

根据Django REST Framework文档

ModelSerializer上的write_only_fields选项已移至PendingDeprecation,并已替换为更通用的extra_kwargs

这就是为什么建议这样做的原因:您应该使用extra_kwargs:

extra_kwargs = {
    'model_b_ids': {'write_only': True},
    'another_field': {'read_only': True}
}

要么:

 model_b_ids = serializers.IntegerField(write_only=True)

2
我刚刚将DRF从3.1.3更新为3.3.3,然后突然发回了我的用户密码(我在write_onlyMeta
atribute

13
@vabada我希望您的意思是哈希😅–
RobinJ

6

可能您正在监督自己ModelA是否拥有该财产modelb_set。在Django中,您在一个模型类中描述了这种关系。Django通过降低目标模型的大小写并为其加后缀来提供向后的关系_set。因此,您可以执行以下操作:

a = ModelA.objects.get(pk=1)
a.modelb_set.all()

这将从ModelA获得ID(或主键)为1的元素,并检索所有相关的ModelB元素。

您可以设置一个值related_name来覆盖默认值:

class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA, related_name='model_b')

在DRF中,您可以稍微调整串行器:

class ASerializer(serializers.ModelSerializer):
    model_b = serializers.PrimaryKeyRelatedField(many=True, read_only=False)

    class Meta:
        model = ModelA
        write_only_fields = ('model_b',)

随着serializers.CharField()您不能发表值,并将其写入模式,因为它不是一个模型字段。

试试这个例子。修补匠和实验。它应该使您更接近解决方案。

编辑:我不太确定Django如何为PascalCase类名创建向后关系的名称。是model_b_set为了ModelB吗?还是modelb_set吗?您可以尝试找出来。


5

您可以从docs使用read_only

只读字段包含在API输出中,但在创建或更新操作期间不应包含在输入中。错误地包含在序列化器输入中的所有“ read_only”字段都将被忽略。

将此设置为True以确保序列化表示形式时使用该字段,而在反序列化期间创建或更新实例时不使用该字段。

默认为 False

例如:

我们可以在序列化器字段上使用它:

model_b_ids = serializers.IntegerField(read_only=True)

或者我们可以在extra_kwargs

extra_kwargs = {
    'model_b_ids': {'read_only': True}
}

Ellusive,但这就是它在Django 3.1中的工作方式
Dark_eye

1

好吧,您可以重写ASerializer上的serializer.save()方法以实例化modelA对象,设置其属性,保存它,然后在现有的modelB对象上设置关系,也保存它们并成功。但是我认为也许像建议的那样在序列化程序上设置related_name和RelatedField会做完全相同的事情....键入更少..总体上更好:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.