2016-06-15 12 views
6

Ich bin neu in DRF. Ich habe die API-Dokumentation gelesen, vielleicht ist sie mir nicht bewusst, aber ich konnte keinen praktischen Weg finden, dies zu tun.Django Rest Framework POST Update, wenn vorhanden oder erstellen

Ich habe ein Antwortobjekt, das eine Eins-zu-Eins-Beziehung mit einer Frage hat.

Auf der Vorderseite verwendete ich die POST-Methode, um eine Antwort an api/Antworten zu senden, und PUT-Methode zum Aktualisieren an z. api/Antworten/24

Aber ich möchte es auf der Serverseite behandeln. Ich werde nur eine POST-Methode an API/Antworten senden, und DRF wird basierend auf Antwort_ID oder Frage_ID prüfen (da es eins zu eins ist), wenn das Objekt existiert. Wenn dies der Fall ist, wird das vorhandene aktualisiert, wenn nicht, wird eine neue Antwort erstellt.

Wo ich es implementieren sollte, konnte ich nicht herausfinden. Überschreiben erstellen in Serializer oder in ViewSet oder etwas anderes?

Mein Modell, Serializer und Ansicht sind diese:

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

Wird POST [bearbeiten Sie das Objekt] (http://restcookbook.com/HTTP%20Methods/put-vs-post/) nicht, wenn es bereits einen gibt (sofern die ID in der URL erwähnt wird) ?. Von dem Link: "_ Es ist durchaus möglich, gültig und sogar in einigen Fällen bevorzugt, PUT zu verwenden, um Ressourcen zu erstellen, oder POST zu verwenden, um Ressourcen zu aktualisieren _". – LaundroMat

+0

Nein, der Artikel sagt, wenn Sie die ID in der URL angeben, verwenden Sie PUT, sonst verwenden Sie POST. Also ich möchte POST verwenden. Aber ich möchte, dass das Update nicht versucht zu erstellen, wenn es bereits diese Instanz gibt. Und ich möchte, dass es teilweise aktualisiert wird, also gibt es auch das. –

+0

Hm - ich muss das falsch interpretiert haben. Ich sehe aus den Kommentaren des Artikels, dass ich nicht alleine bin :) Vielleicht kann dir diese [vorherige SO-Antwort] (http://stackoverflow.com/a/18243587/308204) dann weiterhelfen? – LaundroMat

Antwort

5

Antwort von posted @Nirri half mir auch, aber ich habe elegantere Lösung gefunden Django QuerySet API Abkürzung

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 

Es tut genau das Gleiche - wenn Answer dem Question nicht existiert, wird erstellt, sonst - wie von question Feldsuche zurückgegeben.

Diese Verknüpfung aktualisiert das Objekt jedoch nicht. QuerySet API hat eine andere Methode für eine update Operation, die update_or_create genannt und in other answer down the thread veröffentlicht wird.

+0

Denkst du, einen Kommentar zum Downvote abzugeben? – Nevertheless

+0

Wie in der Antwort von K Moe erwähnt, aktualisiert dies das Objekt nicht mit den neuen Werten. https://StackOverflow.com/a/43393253/238166 – Inti

+1

@Inti: Sicher es nicht. Es folgt einem traditionelleren REST-Ansatz und dient als Referenz für eine sauberere Lösung mit Verknüpfungen, die ** Teil der API sind, auf die ich einen Link zu ** gepostet habe. Natürlich werde ich die Antwort aktualisieren, um Missverständnisse zu vermeiden. – Nevertheless

3

ich die Serializer verwenden würde Methode erstellen.

Darin können Sie überprüfen, ob die Frage (mit der ID, die Sie im Feld 'Frage' Primärschlüssel angeben) bereits eine Antwort hat, und falls dies der Fall ist, holen Sie das Objekt und aktualisieren Sie es ein neues.

So ist die erste Option würde so etwas wie:

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 

Zweite Möglichkeit zu prüfen wäre, wenn die Antwort mit der Antwort-ID vorhanden ist.

Antwort-IDs zeigen würde nicht in den validierten Daten der post-Anfragen, es sei denn Sie eine Art der Problemumgehung verwendet und manuell definiert sie als read_only = false Felder:

id = serializers.IntegerField(read_only=False) 

Aber Sie sollten jedoch überdenken diese durch Es gibt einen guten Grund, warum die PUT-Methode und die POST-Methoden als separate Entitäten existieren, und Sie sollten die Anfragen am Frontend trennen.

+3

Vielen Dank. Die erste Option wäre ausreichend. Der Grund, warum ich sie trennen möchte, ist dies: Wenn es eine Antwort gibt, rufe ich UpdateAnswer sonst CreateAnswer auf der Vorderseite auf - es ist in React BTW. Stellen wir uns also vor, Sie haben die Website in verschiedenen Browsern geöffnet. Es gibt eine unbeantwortete Frage, und Sie haben auf die Schaltfläche "JA" geklickt und Daten gesendet, sodass der Server das Objekt erstellt. Ich habe den Browser nicht aktualisiert und auf "JA" oder "NEIN" geklickt, sodass eine weitere Erstellungsanforderung gesendet wird, die fehlschlägt. Also muss ich auf der Serverseite damit umgehen. Deshalb. –

10

Leider beantwortet Ihre bereitgestellte und akzeptierte Antwort Ihre ursprüngliche Frage nicht, da das Modell nicht aktualisiert wird.Dies ist jedoch leicht durch eine andere bequeme Methode erreicht: 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 

Dies sollte ein Answer Objekt in der Datenbank erstellen, wenn man mit question=validated_data['question'] nicht mit der von validated_data['answer'] genommen Antwort gibt. Wenn es bereits existiert, setzt django sein Antwortattribut auf validated_data['answer'].

Wie in der Antwort von Nirri erwähnt, sollte diese Funktion innerhalb des Serialisierers liegen. Wenn Sie den generischen Code ListCreateView verwenden, ruft er die create-Funktion auf, sobald eine Post-Anforderung gesendet wurde, und generiert die entsprechende Antwort.

Verwandte Themen