質問

オブジェクトにデータを逆シリアル化しようとすると、データベース内のオブジェクトにすでに割り当てられている値を既に割り当てる値を指定した場合は、キー制約エラーが発生します。これは、すでに使用されている固有の値を持つオブジェクトを作成しようとしているため、理にかなっています。

ModelSerializerのGET_OR_CREATEタイプの機能を持つ方法はありますか?シリアライザに一部のデータを入力できるようになり、指定された固有フィールドを持つオブジェクトが存在する場合は、そのオブジェクトを返します。

役に立ちましたか?

解決

私の経験では、NmgeekのソリューションはSerializer.is_valid()としてDRF 3+では機能しません.IS_VALID()モデルのUnique_Togetherの制約を正しく尊重します。これを回避するには、UniquetogetherValidatorを削除し、シリアライザのCREATEメソッドをオーバーライドすることで機能できます。

class MyModelSerializer(serializers.ModelSerializer):

    def run_validators(self, value):
        for validator in self.validators:
            if isinstance(validator, validators.UniqueTogetherValidator):
                self.validators.remove(validator)
        super(MyModelSerializer, self).run_validators(value)

    def create(self, validated_data):
        instance, _ = models.MyModel.objects.get_or_create(**validated_data)
        return instance

    class Meta:
        model = models.MyModel
.

他のヒント

3.0バージョンのREST Frameworkから開始して、シリアライザRestore_Objectメソッドを削除しました。

GET_OR_CREATE機能を追加するための簡単な方法は次のとおりです。

class MyObjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyObject
        fields = (
                  'unique_field',
                  'other_field',
                  )

    def get_or_create(self):
        defaults = self.validated_data.copy()
        identifier = defaults.pop('unique_field')
        return MyObject.objects.get_or_create(unique_field=identifier, defaults=defaults)

def post(self, request, format=None):
    serializer = MyObjectSerializer(data=request.data)
    if serializer.is_valid():
        instance, created = serializer.get_or_create()
        if not created:
            serializer.update(instance, serializer.validated_data)
        return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
.

しかし、照会の結果に応じて、インスタンスが存在する場合に照会または保存した場合よりも、結果のコードが照会または保存されている場合よりも、結果として得られるコードがさらにコンパクトで理解しやすいことはわかりません。

シリアライザがビューで受信されたデータに基づいてオブジェクトを取得または作成できるようにする必要があるシナリオはいくつかあります - 検索/作成機能を実行するためのビューの論理的ではありません - 私はこれに遭遇しました週

はいシリアライザにget_or_create機能を持つことが可能です。これについてはここでのドキュメントにヒントがあります。 rel="nofollow"> http://www.django-rest-framework.org/api-guide/serializeRizerS#.Specification-Fields-should-be-write-only

  • restore_objectメソッドは、新しいユーザーをインスタンス化するために書かれています。
  • instance属性は、このメソッドがユーザーの更新に使用されていないことを確認するためにNoneとして固定されています。

これでさらにget_or_createrestore_objectに入れることができると思います。このインスタンスでは、このインスタンスのユーザーの読み込みユーザーのロード:

class UserFromEmailSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = [
            'email',
        ]

    def restore_object(self, attrs, instance=None):
        assert instance is None, 'Cannot update users with UserFromEmailSerializer'

        (user_object, created) = get_user_model().objects.get_or_create(
            email=attrs.get('email')
        )

        # You can extend here to work on `user_object` as required - update etc.

        return user_object
.

今すぐビューのpostメソッドでシリアライザを使用できます(例:

def post(self, request, format=None):

    # Serialize "new" member's email
    serializer = UserFromEmailSerializer(data=request.DATA)

    if not serializer.is_valid():
        return Response(serializer.errors,
                        status=status.HTTP_400_BAD_REQUEST)

    # Loaded or created user is now available in the serializer object:
    person=serializer.object
    # Save / update etc.
.

@ Groadyの回答は機能しますが、新しいオブジェクトを作成するときに一意性を検証する能力を失った(CICUMSTANCEに関係なくバリデータのリストから削除されました)。シリアライザを使用するという考え全体は、オブジェクトを作成するために使用するデータの整合性を検証する新しいオブジェクトを作成するための包括的な方法があることです。検証を削除するのは、必要なものではありません。新しいオブジェクトを作成するときにこの検証が存在するようにしてください。シリアライザにデータを投げることができるだけで、フード(Get_OR_CREATE)、検証、およびすべて含まれているすべてのものの下の正しい動作を取得します。

代わりにシリアライザ上でis_valid()メソッドを上書きすることをお勧めします。下のコードで、最初にデータベースに存在するかどうかを確認します。存在する場合は、このオブジェクトをシリアライザに添付してから、シリアライザとデータをインスタンス化した場合と同じように検証を続行します。その後、serializer.save()をHIT HEATしてください。 (La get_or_create)。 .is_valid()または.save()を上書きする必要はありません。

ここでの警告は、.create()を押したときにデータベース上で不要な.update()トランザクションを取得することですが、1つの追加のデータベースコールのコストはまだ完全な検証があると依然として価値があるようです。また、カスタムモデル.ManagerおよびCustom Models.QuerySetを使用することもできます。問題のオブジェクトへの更新により、データフィールドのサブセットから固有のオブジェクトをつかみ、残りのフィールドをオブジェクトに更新として扱うことができます(その場合は、UPDATE呼び出しは余分なものではありません)。

.save()への呼び出しはPython3構文にあります。 Python 2を使用している場合は、古いスタイルを使用したい場合:initial_data

from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned


class MyModelSerializer(serializers.ModelSerializer):

    def is_valid(self, raise_exception=False):
        if hasattr(self, 'initial_data'):
            # If we are instantiating with data={something}
            try:
                # Try to get the object in question
                obj = Security.objects.get(**self.initial_data)
            except (ObjectDoesNotExist, MultipleObjectsReturned):
                # Except not finding the object or the data being ambiguous
                # for defining it. Then validate the data as usual
                return super().is_valid(raise_exception)
            else:
                # If the object is found add it to the serializer. Then
                # validate the data as usual
                self.instance = obj
                return super().is_valid(raise_exception)
        else:
            # If the Serializer was instantiated with just an object, and no
            # data={something} proceed as usual 
            return super().is_valid(raise_exception)

    class Meta:
        model = models.MyModel
.

これを行うためのより良い方法は、代わりにPUT動詞を使用してから、get_object()ModelViewSetメソッドをオーバーライドすることです。私はここに答えました: https://stackoverflow.com/a/35024782/3025825

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top