web-dev-qa-db-ja.com

Django RESTフレームワーク-オプションフィールドのシリアル化

オプションのフィールドを持つオブジェクトがあります。私はこの方法でシリアライザーを定義しました:

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を省略すると、正常に機能します。

これを正しく行うにはどうすればよいですか?つまり、オプションのフィールドでオブジェクトをシリアル化します。

27
Aziz Alfoudari

シリアライザーは、フィールドの固定セットを使用するように意図的に設計されているため、オプションでキーの1つを簡単にドロップアウトすることはできません。

SerializerMethodField を使用してフィールド値を返すか、フィールドが存在しない場合はNoneを返すか、シリアライザーをまったく使用できず、単に応答を返すビューを書くことができます直接。

REST framework 3.0_serializer.fields_の更新は、インスタンス化されたシリアライザーで変更できます。おそらく、カスタムSerializer.__init__()メソッドのフィールドを変更することをお勧めします。

13
Tom Christie

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

このアプローチは簡単で柔軟性がありますが、表示されないフィールドをシリアル化するコストがかかります。しかし、それはおそらく大丈夫です。

22
Mark Chackerian

以下に説明する方法で作業を行いました。とてもシンプルで簡単で、私のために働いた。

使用されるDRFバージョン= djangorestframework(3.1.0)

class test(serializers.Serializer):
  id= serializers.IntegerField()
  name=serializers.CharField(required=False,default='some_default_value')
4
RAJ GUPTA

この目的のために、シリアライザーにはpartial引数があります。シリアライザーが初期化されるときに、partial=Trueを渡すことができます。ジェネリックまたはミックスインを使用している場合、次のようにget_serializer関数をオーバーライドできます。

def get_serializer(self, *args, **kwargs):
    kwargs['partial'] = True
    return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)

そして、それはトリックを行います。

注:これにより、特定のフィールドだけでなく、すべてのフィールドをオプションにすることができます。詳細のみが必要な場合は、メソッドをオーバーライド(更新)して、さまざまなフィールドの存在の検証を追加できます。

1
Uri Shalit

「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を使用して、作成プロセス中に特定のリモートサービスから受信した生の応答データの一部を表示できる一時的なアプローチです。将来的には、この機能を維持したいのですが、デフォルトで下位レベルの情報を返すのではなく、作成リクエストの「デバッグ情報のレポート」フラグの後ろに隠します。

0
ncoghlan

DynamicSerializer DRF 3の場合、動的に指定できますどのフィールドをシリアライザで使用するか、除外するか、およびオプションで必須になります!

  1. Mixinを作成する
    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
  1. シリアライザーの初期化/調整 DynamicSerializerMixinを継承に追加することにより

class UserProfileSerializer(DynamicSerializerMixin, serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            "id",
            'first_name', 'last_name'
            "email",
            "is_staff",
        )
  1. 使用する :)
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)
0
pymen