オプションのフィールドを持つオブジェクトがあります。私はこの方法でシリアライザーを定義しました:
class ProductSerializer(serializers.Serializer):
code = serializers.Field(source="Code")
classification = serializers.CharField(source="Classification", required=False)
I 思考required=False
は、フィールドが存在しない場合、フィールドをバイパスするジョブを実行します。ただし、ドキュメントでは、これはシリアル化ではなく逆シリアル化に影響することが記載されています。
次のエラーが表示されます。
'Product' object has no attribute 'Classification'
シリアル化されたインスタンスの.data
にアクセスしようとすると、これが発生します。 (これは、これを引き起こしているのは逆シリアル化であるという意味ではありませんか?)
これは、Classification
を持たないインスタンスで発生します。シリアライザークラスからClassification
を省略すると、正常に機能します。
これを正しく行うにはどうすればよいですか?つまり、オプションのフィールドでオブジェクトをシリアル化します。
シリアライザーは、フィールドの固定セットを使用するように意図的に設計されているため、オプションでキーの1つを簡単にドロップアウトすることはできません。
SerializerMethodField を使用してフィールド値を返すか、フィールドが存在しない場合はNone
を返すか、シリアライザーをまったく使用できず、単に応答を返すビューを書くことができます直接。
REST framework 3.0_serializer.fields
_の更新は、インスタンス化されたシリアライザーで変更できます。おそらく、カスタムSerializer.__init__()
メソッドのフィールドを変更することをお勧めします。
Django REST Framework 3.0 +
動的フィールドがサポートされるようになりました。 http://www.Django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields を参照してください。シリアライザ内のフィールドを選択し、不要なフィールドを選択的に削除できます。
または、シリアライザーの初期化でMeta.fieldsをいじるモデルシリアライザーに対して、次のようなことを行うこともできます。
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('code',)
def __init__(self, *args, **kwargs):
if SHOW_CLASSIFICATION: # add logic here for optional viewing
self.Meta.fields = list(self.Meta.fields)
self.Meta.fields.append('classification')
super(ProductSerializer, self).__init__(*args, **kwargs)
ただし、長期計画には適合しない可能性があるため、これが「正しい方法」であるかどうかをトムに尋ねる必要があります。
Django REST Framework <3.0
次のようなものを試してください。
class ProductSerializer(serializers.Serializer):
...
classification = serializers.SerializerMethodField('get_classification')
def get_classification(self, obj):
return getattr(obj, 'classification', None)
複数のシリアライザー
別のアプローチは、異なるフィールドのセットを持つ複数のシリアライザーを作成することです。あるシリアライザーは別のシリアライザーを継承し、フィールドを追加します。その後、get_serializer_class
メソッドを使用して、ビューで適切なシリアライザーを選択できます。ユーザーオブジェクトが要求ユーザーと同じ場合、このアプローチを使用してさまざまなシリアライザーを呼び出してさまざまなユーザーデータを表示する実際の例を次に示します。
def get_serializer_class(self):
""" An authenticated user looking at their own user object gets more data """
if self.get_object() == self.request.user:
return SelfUserSerializer
return UserSerializer
表現からフィールドを削除する
セキュリティコンテキストで使用した別のアプローチは、to_representation
メソッドのフィールドを削除することです。次のようなメソッドを定義します
def remove_fields_from_representation(self, representation, remove_fields):
""" Removes fields from representation of instance. Call from
.to_representation() to apply field-level security.
* remove_fields: a list of fields to remove
"""
for remove_field in remove_fields:
try:
representation.pop(remove_field)
except KeyError:
# Ignore missing key -- a child serializer could inherit a "to_representation" method
# from its parent serializer that applies security to a field not present on
# the child serializer.
pass
そして、シリアライザーで、そのメソッドを次のように呼び出します
def to_representation(self, instance):
""" Apply field level security by removing fields for unauthorized users"""
representation = super(ProductSerializer, self).to_representation(instance)
if not permission_granted: # REPLACE WITH PERMISSION LOGIC
remove_fields = ('classification', )
self.remove_fields_from_representation(representation, remove_fields)
return representation
このアプローチは簡単で柔軟性がありますが、表示されないフィールドをシリアル化するコストがかかります。しかし、それはおそらく大丈夫です。
以下に説明する方法で作業を行いました。とてもシンプルで簡単で、私のために働いた。
使用されるDRFバージョン= djangorestframework(3.1.0)
class test(serializers.Serializer):
id= serializers.IntegerField()
name=serializers.CharField(required=False,default='some_default_value')
この目的のために、シリアライザーにはpartial
引数があります。シリアライザーが初期化されるときに、partial=True
を渡すことができます。ジェネリックまたはミックスインを使用している場合、次のようにget_serializer
関数をオーバーライドできます。
def get_serializer(self, *args, **kwargs):
kwargs['partial'] = True
return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)
そして、それはトリックを行います。
注:これにより、特定のフィールドだけでなく、すべてのフィールドをオプションにすることができます。詳細のみが必要な場合は、メソッドをオーバーライド(更新)して、さまざまなフィールドの存在の検証を追加できます。
「DRFとDjangoの両方の特定の実装の詳細に依存する恐ろしいハックですが、(少なくとも今のところ)動作する」ファイルから、「作成」メソッドからの応答に追加のデバッグデータを含めるために使用したアプローチを次に示します。シリアライザーでの実装:
def create(self, validated_data)
# Actual model instance creation happens here...
self.fields["debug_info"] = serializers.DictField(read_only=True)
my_model.debug_info = extra_data
return my_model
これは、ブラウズ可能なAPIを使用して、作成プロセス中に特定のリモートサービスから受信した生の応答データの一部を表示できる一時的なアプローチです。将来的には、この機能を維持したいのですが、デフォルトで下位レベルの情報を返すのではなく、作成リクエストの「デバッグ情報のレポート」フラグの後ろに隠します。
DynamicSerializer DRF 3の場合、動的に指定できますどのフィールドをシリアライザで使用するか、除外するか、およびオプションで必須になります!
class DynamicSerializerMixin:
"""
A Serializer that takes an additional `fields` argument that
controls which fields should be used.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop("fields", None)
excluded_fields = kwargs.pop("excluded_fields", None)
required_fields = kwargs.pop("required_fields", None)
# Instantiate the superclass normally
super().__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
if isinstance(fields, dict):
for field, config in fields.items():
set_attrs(self.fields[field], config)
if excluded_fields is not None:
# Drop any fields that are not specified in the `fields` argument.
for field_name in excluded_fields:
self.fields.pop(field_name)
if required_fields is not None:
for field_name in required_fields:
self.fields[field_name].required = True
class UserProfileSerializer(DynamicSerializerMixin, serializers.ModelSerializer):
class Meta:
model = User
fields = (
"id",
'first_name', 'last_name'
"email",
"is_staff",
)
class RoleInvitationSerializer(serializers.ModelSerializer):
invited_by = UserProfileSerializer(fields=['id', 'first_name', 'last_name'])
またはin action apis
@action(detail=True, serializer_class=YourSerialzierClass)
def teams_roles(self, request, pk=None):
user = self.get_object()
queryset = user.roles.all()
serializer = self.get_serializer(queryset, many=True, excluded_fields=['user'])
return Response(data=serializer.data)