Django DRF:read_only_fields 无法正常工作
Django DRF: read_only_fields not working properly
我有以下型号
class Breed(models.Model)::
name = models.CharField(max_length=200)
class Pet(models.Model):
owner = models.ForeignKey(
"User",
on_delete=models.CASCADE,
)
name = models.CharField(max_length=200)
breed = models.ForeignKey(
"Breed",
on_delete=models.CASCADE,
)
出于 representation
目的,我正在尝试添加一些文件。我不希望在 create
或 update
时包含它们
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email')
breed_name = serializers.CharField(source='breed.str')
class Meta:
model = Pet
fields = "__all__"
read_only_fields = ["breed_name","owner_email"]
这是行不通的。我在 HTML 表单(DRF api 页面)
中看到 owner_email
和 breed_name
其中
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email',read_only=True)
breed_name = serializers.CharField(source='breed.str',read_only=True)
class Meta:
model = Pet
fields = "__all__"
这是有效的。我没有在 HTMLform
中看到它们
我还观察到,如果我直接在 read_only_fields 中使用模型字段,那么它会起作用。
class PetSerializer(serializers.ModelSerializer):
class Meta:
model = Pet
fields = "__all__"
read_only_fields = ["name"]
这将使所有 name
未显示在更新或创建中
为什么 read_only_fields
不能正常工作
read_only_fields
元选项将适用于 的字段未在 Serializer.
中明确定义
因此,在您的情况下,您需要将 read_only=True
添加到那些明确定义的字段中,如
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email', <b>read_only=True</b>)
breed_name = serializers.CharField(source='breed.str', <b>read_only=True</b>)
class Meta:
model = Pet
fields = "__all__"
这很有趣。我查看了代码并找到了根本原因,特别是这个 lines in the implementation for ModelSerializer:
for field_name in field_names:
# If the field is explicitly declared on the class then use that.
if field_name in declared_fields:
fields[field_name] = declared_fields[field_name]
continue
....
这是我的调查脚本
from django.db import models
from rest_framework import serializers
class MyModel(models.Model):
xero_contact_id = models.UUIDField(unique=True)
name = models.CharField(max_length=255, default="Some name")
class Meta:
db_table = "my_model"
class MySerializer(serializers.ModelSerializer):
owner_email = serializers.CharField()
breed_name = serializers.CharField(max_length=255)
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ["breed_name", "owner_email", "xero_contact_id"]
serializer = MySerializer()
print(repr(serializer))
我添加了一些印刷品,这是我看到的:
>>> print(repr(serializer))
field_names ['id', 'owner_email', 'breed_name', 'xero_contact_id', 'name']
declared_fields OrderedDict([('owner_email', CharField()), ('breed_name', CharField(max_length=255))])
extra_kwargs {'breed_name': {'read_only': True}, 'owner_email': {'read_only': True}, 'xero_contact_id': {'read_only': True}}
MySerializer():
id = IntegerField(label='ID', read_only=True)
owner_email = CharField()
breed_name = CharField(max_length=255)
xero_contact_id = UUIDField(read_only=True)
name = CharField(max_length=255, required=False)
如您所见,read_only
参数在 extra_kwargs
中。问题在于,对于仅在 ModelSerializer
本身(从 declared_fields
中可见)而不是在模型 class 中声明的所有字段,它们不会从 [=16] 中读取=],他们只是读取在上面的代码片段中可见的字段本身设置的内容 fields[field_name] = declared_fields[field_name]
,然后执行 continue
。因此,read_only
的选项被忽略了。
我通过修改 ModelSerializer
的实现来修复它,即使对于非模型字段也考虑 extra_kwargs
for field_name in field_names:
# If the field is explicitly declared on the class then use that.
if field_name in declared_fields:
field_class = type(declared_fields[field_name])
declared_field_args = declared_fields[field_name].__dict__['_args']
declared_field_kwargs = declared_fields[field_name].__dict__['_kwargs']
extra_field_kwargs = extra_kwargs.get(field_name, {})
# Old implementation doesn't take into account the extra_kwargs
# fields[field_name] = declared_fields[field_name]
# New implementation takes into account the extra_kwargs
fields[field_name] = field_class(*declared_field_args, **declared_field_kwargs, **extra_field_kwargs)
continue
....
现在,read_only
已正确设置为目标字段,包括非模型字段:
>>> print(repr(serializer))
field_names ['id', 'owner_email', 'breed_name', 'xero_contact_id', 'name']
declared_fields OrderedDict([('owner_email', CharField()), ('breed_name', CharField(max_length=255))])
extra_kwargs {'breed_name': {'read_only': True}, 'owner_email': {'read_only': True}, 'xero_contact_id': {'read_only': True}}
MySerializer():
id = IntegerField(label='ID', read_only=True)
owner_email = CharField(read_only=True)
breed_name = CharField(max_length=255, read_only=True)
xero_contact_id = UUIDField(read_only=True)
name = CharField(max_length=255, required=False)
这似乎不在 DRF 文档中。听起来像是我们可以向 DRF 请求的功能 :) 所以同时的解决方案正如@JPG 指出的那样,在额外的非模型字段中明确使用 read_only=True
。
我有以下型号
class Breed(models.Model)::
name = models.CharField(max_length=200)
class Pet(models.Model):
owner = models.ForeignKey(
"User",
on_delete=models.CASCADE,
)
name = models.CharField(max_length=200)
breed = models.ForeignKey(
"Breed",
on_delete=models.CASCADE,
)
出于 representation
目的,我正在尝试添加一些文件。我不希望在 create
或 update
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email')
breed_name = serializers.CharField(source='breed.str')
class Meta:
model = Pet
fields = "__all__"
read_only_fields = ["breed_name","owner_email"]
这是行不通的。我在 HTML 表单(DRF api 页面)
中看到owner_email
和 breed_name
其中
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email',read_only=True)
breed_name = serializers.CharField(source='breed.str',read_only=True)
class Meta:
model = Pet
fields = "__all__"
这是有效的。我没有在 HTMLform
中看到它们我还观察到,如果我直接在 read_only_fields 中使用模型字段,那么它会起作用。
class PetSerializer(serializers.ModelSerializer):
class Meta:
model = Pet
fields = "__all__"
read_only_fields = ["name"]
这将使所有 name
未显示在更新或创建中
为什么 read_only_fields
不能正常工作
read_only_fields
元选项将适用于 的字段未在 Serializer.
因此,在您的情况下,您需要将 read_only=True
添加到那些明确定义的字段中,如
class PetSerializer(serializers.ModelSerializer):
owner_email = serializers.CharField(source='owner.email', <b>read_only=True</b>)
breed_name = serializers.CharField(source='breed.str', <b>read_only=True</b>)
class Meta:
model = Pet
fields = "__all__"
这很有趣。我查看了代码并找到了根本原因,特别是这个 lines in the implementation for ModelSerializer:
for field_name in field_names:
# If the field is explicitly declared on the class then use that.
if field_name in declared_fields:
fields[field_name] = declared_fields[field_name]
continue
....
这是我的调查脚本
from django.db import models
from rest_framework import serializers
class MyModel(models.Model):
xero_contact_id = models.UUIDField(unique=True)
name = models.CharField(max_length=255, default="Some name")
class Meta:
db_table = "my_model"
class MySerializer(serializers.ModelSerializer):
owner_email = serializers.CharField()
breed_name = serializers.CharField(max_length=255)
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ["breed_name", "owner_email", "xero_contact_id"]
serializer = MySerializer()
print(repr(serializer))
我添加了一些印刷品,这是我看到的:
>>> print(repr(serializer))
field_names ['id', 'owner_email', 'breed_name', 'xero_contact_id', 'name']
declared_fields OrderedDict([('owner_email', CharField()), ('breed_name', CharField(max_length=255))])
extra_kwargs {'breed_name': {'read_only': True}, 'owner_email': {'read_only': True}, 'xero_contact_id': {'read_only': True}}
MySerializer():
id = IntegerField(label='ID', read_only=True)
owner_email = CharField()
breed_name = CharField(max_length=255)
xero_contact_id = UUIDField(read_only=True)
name = CharField(max_length=255, required=False)
如您所见,read_only
参数在 extra_kwargs
中。问题在于,对于仅在 ModelSerializer
本身(从 declared_fields
中可见)而不是在模型 class 中声明的所有字段,它们不会从 [=16] 中读取=],他们只是读取在上面的代码片段中可见的字段本身设置的内容 fields[field_name] = declared_fields[field_name]
,然后执行 continue
。因此,read_only
的选项被忽略了。
我通过修改 ModelSerializer
的实现来修复它,即使对于非模型字段也考虑 extra_kwargs
for field_name in field_names:
# If the field is explicitly declared on the class then use that.
if field_name in declared_fields:
field_class = type(declared_fields[field_name])
declared_field_args = declared_fields[field_name].__dict__['_args']
declared_field_kwargs = declared_fields[field_name].__dict__['_kwargs']
extra_field_kwargs = extra_kwargs.get(field_name, {})
# Old implementation doesn't take into account the extra_kwargs
# fields[field_name] = declared_fields[field_name]
# New implementation takes into account the extra_kwargs
fields[field_name] = field_class(*declared_field_args, **declared_field_kwargs, **extra_field_kwargs)
continue
....
现在,read_only
已正确设置为目标字段,包括非模型字段:
>>> print(repr(serializer))
field_names ['id', 'owner_email', 'breed_name', 'xero_contact_id', 'name']
declared_fields OrderedDict([('owner_email', CharField()), ('breed_name', CharField(max_length=255))])
extra_kwargs {'breed_name': {'read_only': True}, 'owner_email': {'read_only': True}, 'xero_contact_id': {'read_only': True}}
MySerializer():
id = IntegerField(label='ID', read_only=True)
owner_email = CharField(read_only=True)
breed_name = CharField(max_length=255, read_only=True)
xero_contact_id = UUIDField(read_only=True)
name = CharField(max_length=255, required=False)
这似乎不在 DRF 文档中。听起来像是我们可以向 DRF 请求的功能 :) 所以同时的解决方案正如@JPG 指出的那样,在额外的非模型字段中明确使用 read_only=True
。