web-dev-qa-db-ja.com

Django restフレームワークの書き込み専用、読み取り専用フィールド

このようなモデルがあります:

class ModelA(models.Model):
    name = models.CharField()


class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA)

シリアライザー:

class ASerializer(serializers.ModelSerializer):
    model_b_ids = serializers.CharField()
    class Meta:
        model = ModelA
        write_only_fields = ('model_b_ids',)

ビュー:

class AView(CreateModelMixin, GenericViewSet):

    def perform_create(self, serializer): 
        model_b_ids = parse_somehow(serializer.validated_data["model_b_ids"])
        #do something...

私が得ている問題は、「model_b_ids」にあります

ユーザーは、投稿データを送信するときに送信する必要があります。

Perform_createで使用して、関連するモデルにリンクします。

しかし、ModelAの「実際の列」ではないため、保存しようとすると例外が発生します。

Validated_dataからポップしようとしましたが、modelからmodel_b_idsを読み取れないエラーが再び発生しました。この種のフィールドを正しく使用するためのアイデアはありますか?

46
user1305989

Imが間違っているが、Django restフレームワークにMeta属性があるとは思わない場合は修正してください

write_only_fields

docsに従って、extra_kwargsに書き込み専用フィールドを設定します

例えば

class UserSerializer(ModelSerializer):
    """
    ``Serializer`` for ``User`` ..
    """

    class Meta:
        model = User
        fields = ('id', 'email', 'first_name', 'last_name' ,'security_question', 'security_question_answer', 'password', 'is_active', 'is_staff')
        read_only_fields = ('is_active', 'is_staff')
        extra_kwargs = {
            'security_question': {'write_only': True},
            'security_question_answer': {'write_only': True},
            'password': {'write_only': True}
        }
64
Dr Manhattan

Django REST Frameworkに準拠 ドキュメント

ModelSerializerのwrite_only_fieldsオプションはPendingDeprecationに移動され、より一般的なextra_kwargsに置き換えられました

そのため、次のようにすることをお勧めします。extra_kwargsを使用する必要があります。

extra_kwargs = {'model_b_ids': {'write_only': True}}

または:

 model_b_ids = serializers.IntegerField(write_only=True)
42
M.Void

おそらく、あなたのModelAにはプロパティmodelb_setがあることを監視しているでしょう。 Djangoでは、1つのモデルクラスで関係を記述します。 Djangoは、ターゲットモデルを小文字にし、_setで接尾辞を付けることにより、後方関係を提供します。だからあなたができる:

a = ModelA.objects.get(pk=1)
a.modelb_set.all()

これにより、ModelAからID(または主キー)1の要素が取得され、関連するすべてのModelB要素が取得されます。

related_nameの値を設定して、デフォルト値を上書きできます。

class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA, related_name='model_b')

DRFでは、シリアライザーをわずかに調整できます。

class ASerializer(serializers.ModelSerializer):
    model_b = serializers.PrimaryKeyRelatedField(many=True, read_only=False)

    class Meta:
        model = ModelA
        write_only_fields = ('model_b',)

serializers.CharField()を使用すると、モデルフィールドではないため、値を投稿してモデルに書き込むことはできません。

この例を試してみてください。ティンカーと実験。ソリューションに近づくはずです。

編集:DjangoがPascalCaseクラス名の後方関係の名前をどのように作成するのか本当にわかりません。 ModelBmodel_b_setですか?それともmodelb_setですか?あなたはそれを試してみることができます。

2
cezar

docs から使用できますread_only

読み取り専用フィールドはAPI出力に含まれますが、作成または更新操作中に入力に含まれるべきではありません。シリアライザー入力に誤って含まれている「read_only」フィールドは無視されます。

これをTrueに設定すると、リプレゼンテーションをシリアル化するときにフィールドが使用されますが、逆シリアル化中にインスタンスを作成または更新するときには使用されません。

デフォルトはFalseです

例として:

Serializerフィールドで使用できます:

model_b_ids = serializers.IntegerField(read_only=True)

または、extra_kwargsで使用できます:

extra_kwargs = {
    'model_b_ids': {'read_only': True}
}
1
Druta Ruslan

ASerializerのserializer.save()メソッドをオーバーライドして、modelAオブジェクトのインスタンスを作成し、その属性を設定し、保存してから、既存のmodelBオブジェクトにリレーションを設定し、同様に保存して成功させます。しかし、多分、示唆されているように、関連付けられたrelated_nameとRelatedFieldをシリアライザーに設定すると、まったく同じことを行うと思います。

0
johnnyB