ネストされたシリアライザーがある場合:
class ChildSerializer(ModelSerializer):
class Meta:
fields = ('c_name', )
model = Child
class ParentSerializer(ModelSerializer):
child = ChildSerializer(many=True, read_only=True)
class Meta:
model = Parent
fields = ('p_name', 'child')
ネストされたシリアライザーのコンテキストにアクセスしたいのですが、どうすればよいですか?私が知る限り、コンテキストは子に渡されません。
ModelSerializerのget_fields()メソッドをオーバーライドするために、フィールドにユーザーごとにアクセス許可モデルを実装できるようにしたいです。
def get_fields(self):
fields = super().get_fields()
....
for f in fields:
if has_rights(self.context['request'].user, f, "read"):
ret_val[f] = fields[f]
....
return ret_val
これは通常のシリアライザーで機能しますが、ネストされた子がget_fields()に渡されると、コンテキスト、つまりリクエストとユーザーは使用できません。シリアライザーがネストされているときにコンテキストにアクセスするにはどうすればよいですか?
わかりました、私は実用的な解決策を見つけました。 ParentクラスのChildSerializer割り当てを、コンテキストを追加するSerializerMethodFieldに置き換えました。次に、これは私のCustomModelSerializerのget_fieldsメソッドに渡されます。
class ChildSerializer(CustomModelSerializer):
class Meta:
fields = ('c_name', )
model = Child
class ParentSerializer(CustomModelSerializer):
child = serializers.SerializerMethodField('get_child_serializer')
class Meta:
model = Parent
fields = ('p_name', 'child')
def get_child_serializer(self, obj):
serializer_context = {'request': self.context.get('request') }
children = Child.objects.all().filter(parent=obj)
serializer = ChildSerializer(children, many=True, context=serializer_context)
return serializer.data
そして私のCustomModelSerializerで:
class CustomModelSerializer(rest_serializer_classes.HyperlinkedModelSerializer):
def __init__(self, *args, **kwargs):
"""
Make sure a user is coupled to the serializer (needed for permissions)
"""
super().__init__(*args, **kwargs)
if not self.context:
self._context = getattr(self.Meta, 'context', {})
try:
self.user = self.context['request'].user
except KeyError:
self.user = None
def get_fields(self):
ret = OrderedDict()
if not self.user:
print("No user associated with object")
return ret
fields = super().get_fields()
# Bypass permission if superuser
if self.user.is_superuser:
return fields
for f in fields:
if has_right(self.user, self.Meta.model.__name__.lower(), f, "read"):
ret[f] = fields[f]
return ret
これは正常に機能しているようで、Child.c_nameまたはParent.childの読み取り権を取り消すと、子のフィールドはシリアライザーで破棄されます。
代わりにserialziers.ListField
を使用できます。 ListField
は、コンテキストをその子に自動的に渡します。だから、ここにあなたのコードがあります
class ChildSerializer(ModelSerializer):
class Meta:
fields = ('c_name', )
model = Child
class ParentSerializer(ModelSerializer):
child = serializers.ListField(read_only=True, child=ChildSerializer())
class Meta:
model = Parent
fields = ('p_name', 'child')
これは古い質問ですが、2019年にも同じ質問がありました。解決策は次のとおりです。
_class MyBaseSerializer(serializers.HyperlinkedModelSerializer):
def get_fields(self):
'''
Override get_fields() method to pass context to other serializers of this base class.
If the context contains query param "omit_data" as set to true, omit the "data" field
'''
fields = super().get_fields()
# Cause fields with this same base class to inherit self._context
for field_name in fields:
if isinstance(fields[field_name], serializers.ListSerializer):
if isinstance(fields[field_name].child, MyBaseSerializer):
fields[field_name].child._context = self._context
Elif isinstance(fields[field_name], MyBaseSerializer):
fields[field_name]._context = self._context
# Check for "omit_data" in the query params and remove data field if true
if 'request' in self._context:
omit_data = self._context['request'].query_params.get('omit_data', False)
if omit_data and omit_data.lower() in ['true', '1']:
fields.pop('data')
return fields
_
上記では、get_fields()
をオーバーライドし、_self._context
_を同じ基本クラスを持つ子シリアライザーに渡すシリアライザー基本クラスを作成します。 ListSerializersの場合、コンテキストをその子に添付します。
次に、クエリパラメータ「omit_data」を確認し、要求された場合は「data」フィールドを削除します。
これがまだこれに対する答えを探している人にとって役立つことを願っています。