2016-06-15 22 views
6

DRFを初めて使用しています。 私はAPIドキュメントを読んでいますが、それは気にしないかもしれませんが、便利な方法を見つけることができませんでした。Django Rest Framework POSTが存在する場合は更新します。

質問に1対1の関係を持つ回答オブジェクトがあります。

フロントサイドでは、私はPOSTメソッドを使用して、api/answersに送信された回答を作成しました。 api/answers/24

しかし、私はそれをサーバー側で処理したいと思います。私はapi/answersにPOSTメソッドを送信し、オブジェクトが存在する場合、DRFはanswer_idまたはquestion_idに基づいてチェックします(1対1なので)。 もしそうであれば、それは既存のものを更新し、そうでなければ新しい回答を作成します。

私はそれを実装する必要がありますが、私は理解できませんでした。シリアライザやViewSetなどでcreateをオーバーライドしますか?

私のモデル、シリアライザおよびビューは、これらの通りです:

class Answer(models.Model): 
    question = models.OneToOneField(Question, on_delete=models.CASCADE, related_name='answer') 
    answer = models.CharField(max_length=1, 
           choices=ANSWER_CHOICES, 
           null=True, 
           blank=True) 

class AnswerSerializer(serializers.ModelSerializer): 
    question = serializers.PrimaryKeyRelatedField(many=False, queryset=Question.objects.all()) 

    class Meta: 
     model = Answer 
     fields = (
      'id', 
      'answer', 
      'question', 
     ) 

class AnswerViewSet(ModelViewSet): 
    queryset = Answer.objects.all() 
    serializer_class = AnswerSerializer 
    filter_fields = ('question', 'answer',) 
+1

すでにオブジェクトが存在する場合は、[オブジェクトの編集](http://restcookbook.com/HTTP%20Methods/put-vs-post/)しないでください(URLにIDが記載されている場合)。リンクから:「可能な限り、有効であり、場合によってはPUTを使用してリソースを作成したり、POSTを使用してリソースを更新することも好ましい」 – LaundroMat

+0

記事には、urlにidを指定した場合は、PUTを使用してください。そうでない場合は、POSTを使用してください。だから私はPOSTを使いたい。しかし、すでにそのインスタンスが存在する場合、作成しようとしていないものを更新する必要があります。私はそれを部分的に更新したいので、それもあります。 –

+0

Hm - 私はそれを間違って解釈しているに違いない。私は記事のコメントから、私が一人ではないことを知っている:)おそらく、この[以前のSOの回答](http://stackoverflow.com/a/18243587/308204)はあなたに沿ってあなたを助けることができますか? – LaundroMat

答えて

5

回答は、同様に私を助けてくれたが、私はジャンゴQuerySet APIショートカットを使用して、よりエレガントな解決策を見つけた:

def create(self, validated_data): 
    answer, created = Answer.objects.get_or_create(
     question=validated_data.get('question', None), 
     defaults={'answer': validated_data.get('answer', None)}) 

    return answer 

それはまったく同じことをして - そのQuestionAnswerがない場合存在する場合は作成され、それ以外の場合はquestionフィールドルックアップによって返されます。

ただし、このショートカットはオブジェクトを更新しません。 QuerySet APIには、update_or_createと呼ばれ、other answer down the threadに掲載されたupdateの別の方法があります。

+0

downvoteにコメントしていますか? – Nevertheless

+0

K Moeの答えに記載されているように、これは渡された新しい値でオブジェクトを更新しません。 https://stackoverflow.com/a/43393253/238166 – Inti

+1

@Inti:そうではありません。より伝統的なRESTアプローチに従い、APIの一部であるショートカットを使用したよりクリーンなソリューションのリファレンスとして機能します**へのリンクを投稿しました。もちろん、私は誤解を避けるために答えを更新します。 – Nevertheless

3

私は、メソッドを作成するシリアライザを使用します。

質問に「質問」主キーの関連フィールドに入力したIDで質問に回答があるかどうかを確認し、そうであればオブジェクトを取得して更新します。新しいもの

class AnswerSerializer(serializers.ModelSerializer): 
    question = serializers.PrimaryKeyRelatedField(many=False, queryset=Question.objects.all()) 

    class Meta: 
     model = Answer 
     fields = (
      'id', 
      'answer', 
      'question', 
     ) 

def create(self, validated_data): 
    question_id = validated_data.get('question', None) 
    if question_id is not None: 
     question = Question.objects.filter(id=question_id).first() 
     if question is not None: 
      answer = question.answer 
      if answer is not None: 
       # update your answer 
       return answer 

    answer = Answer.objects.create(**validated_data) 
    return answer 

番目のオプションは、アンサーIDと答えが存在するかどうかを確認するために、次のようになりますように

だから、最初の選択肢は何かを行くだろう。あなたにREAD_ONLY = falseのフィールド回避策のようなものを使用して手動で定義されていない限り

回答IDは、POSTリクエストの検証データには表示されません。

id = serializers.IntegerField(read_only=False) 

をしかし、あなたはしかし介してこれを再考すべきですPUTメソッドとPOSTメソッドが別々のエンティティとして存在するのは良い理由です。フロントエンドでリクエストを分離する必要があります。 @Nirriによって投稿

+3

ありがとうございました。最初のオプションで十分でしょう。私がそれらを分離したい理由はこれです:答えがある場合、私はUpdateAnswerを呼び出します。それ以外の場合は、フロントのCreateAnswerがReact btwにあります。それであなたと私は別のブラウザでウェブサイトを開いた想像しましょう。未回答の質問があり、 "YES"ボタンをクリックしてデータを送信し、サーバーがオブジェクトを作成するようにしました。私はブラウザをリフレッシュせず、「はい」または「いいえ」をクリックして、別の作成要求を送信して失敗します。だから私はサーバー側でそれを処理しなければならない。それが理由だった。 –

10

残念なことに、あなたの提供した回答は、モデルを更新しないため、元の質問には答えられません。しかし、これは容易に別の便利な方法によって達成される:update-or-create

def create(self, validated_data): 
    answer, created = Answer.objects.update_or_create(
     question=validated_data.get('question', None), 
     defaults={'answer': validated_data.get('answer', None)}) 
    return answer 
question=validated_data['question']

有するものがvalidated_data['answer']から採取された回答と存在しない場合、これはデータベース内のAnswerオブジェクトを作成する必要があります。既に存在する場合、djangoはその回答属性をvalidated_data['answer']に設定します。

Nirriの答えに記されているように、この関数はシリアライザ内に存在する必要があります。ジェネリックListCreateViewを使用すると、ポストリクエストが送信されるとcreate関数が呼び出され、対応するレスポンスが生成されます。

関連する問題