如何在 django rest 框架中自动填充只读序列化程序字段?

How to auto populate a read-only serializer field in django rest framework?

我有一个关于 django rest 框架的问题。

大多数时候,我有一个序列化器,它有一些只读字段。例如,考虑下面这个简单的模型:

class PersonalMessage(models.Model):
    sender = models.ForeignKey(User, related_name="sent_messages", ...)
    recipient = models.ForeignKey(User, related_name="recieved_messages", ...)
    text = models.CharField(...)

    def __str__(self) -> str:
        return f"{self.text} (sender={self.sender})"

在此模型中,senderrecipient 的值应由应用程序本身自动提供,用户不应编辑这些字段。好了,现在看看这个序列化器:

class PersonalMessageSerializer(serializers.ModelSerializer):
    class Meta:
        model = PersonalMessage
        fields = '__all__'
        read_only_fields = ('sender', 'recipient')

完美防止用户在senderrecipient字段上设置任意值。但问题是,当这些字段在序列化程序中被标记为只读时,序列化程序将完全忽略传递给这些字段的构造函数的所有值。因此,当我尝试创建模型时,不会为这些字段设置任何值:

PersonalMessageSerializer(data={**request.data, 'sender': ..., 'recipient': ...) # Won't work

在 Django 中防止用户设置任意值并同时自动填充那些受限字段的最佳方法是什么休息框架?

根据您获取这两个对象的方式,您可以使用序列化程序的保存方法来传递它们,它们将自动应用于您正在保存的对象:

sender = User.objects.first()
recipient = User.objects.last()

serializer = PersonalMessageSerializer(data=request.data)
message = serializer.save(sender=sender, recipient=recipient)

kwargs 应该与您模型中的字段名称相匹配,这样才能正常工作。作为参考,看看here

从问题中无法理解您想与 senderrecipient 的关系的哪些字段进行交互,但可以在 Serializer relations Django REST 文档部分。

长话短说,如果您只想与一个字段交互,可以使用 SlugRelatedField,这样您就可以仅使用其中一个字段与关系目标交互。 如果只是id,你可以使用PrimaryKeyRelatedField.

如果您想与多个领域互动,方法是Nested Relationships。您可以在此处为目标关系指定自定义序列化程序,但您必须重写 PersonalMessageSerializer 中的 create() 方法才能从您的关系中创建对象,因为嵌套序列化程序默认是只读的。

您可以像这样覆盖序列化程序上下文;

PersonalMessageSerializer(data={**request.data, context={'sender': sender, 'recipent': recipent})

并在序列化程序中捕获上下文。

class PersonalMessageSerializer(serializers.ModelSerializer):
    class Meta:
        model = PersonalMessage
        fields = '__all__'
        read_only_fields = ('sender', 'recipient')
    def validate(self, attrs):
        attrs = super().validate(attrs)
        attrs['sender'] = self.context['sender']
        attrs['recipent'] = self.context['recipent']
        return attrs

现在 serializer.validated_data 它必须 returns 发件人和收件人。

这就是您如何在创建时将设置设置为默认值,但在 DRF 中仅在之后读取。虽然在这个解决方案中它实际上不会是只读的,它是可写的,但是您现在可以明确控制登录用户可以写的内容,这是最终目标

给定模型

class PersonalMessage(models.Model):
    sender = models.ForeignKey(User,...)
    recipient = models.ForeignKey(User,..)
    text = models.CharField(...)

您首先要创建自己的自定义默认值(我将仅展示一个字段的示例)

# Note DRF already has a CurrentUserDefault you can also use
class CurrentSenderDefault:
    requires_context = True

    def __call__(self, serializer_field):
        return serializer_field.context['request'].user

    def __repr__(self):
        return '%s()' % self.__class__.__name__

接下来你创建自己的字段,它知道过滤器是怎么回事。 此查询集可防止人们设置他们不允许的值。这正是你想要的

class SenderField(serializers.PrimaryKeyRelatedField):

    def get_queryset(self):
        user = self.context['request'].user
        if user:
            queryset = User.objects.filter(id=user.id)
        else:
            queryset = User.objects.none()
        return queryset

你终于开始使用序列化程序了

class PersonalMessageSerializer(serializers.ModelSerializer):
    
    sender = SenderField(default=CurrentSenderDefault())
    recipient = ...

    class Meta:
        model = PersonalMessage
        fields = '__all__'
        read_only_fields = ('sender', 'recipient')