2016-06-15 27 views
6

Sono nuovo al DRF. Ho letto i documenti API, forse è ignaro ma non sono riuscito a trovare un modo pratico per farlo.Django Rest Framework aggiornamento POST se esistente o creare

Ho un oggetto risposta che ha una relazione uno-a-uno con una domanda.

Sul lato anteriore usavo il metodo POST per creare una risposta inviata a API/risposte e il metodo PUT per aggiornare inviato ad es. api/risponde/24

Ma voglio gestirlo sul lato server. Trasmetterò solo un metodo POST a api/risposte e DRF controllerà in base a answer_id o question_id (poiché è uno a uno) se l'oggetto esiste. Se lo fa, aggiornerà quello esistente, se non lo farà creerà una nuova risposta.

Dove dovrei implementarlo, non riuscivo a capirlo. Sovrascrivere crea in serializzatore o in ViewSet o qualcos'altro?

Il mio modello, serializzatore e la vista sono come questi:

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

Non POST [modifica l'oggetto] (http://restcookbook.com/HTTP%20Methods/put-vs-post/) se ce n'è già uno (a condizione che l'ID sia menzionato nell'URL) ?. Dal link: "È abbastanza possibile, valido e persino preferito in alcune occasioni, usare PUT per creare risorse, o usare POST per aggiornare risorse_". – LaundroMat

+0

Nessun articolo dice, se fornisci l'id nell'URL, usa PUT altrimenti usa POST. Quindi voglio usare il POST. Ma voglio aggiornarlo non cercando di creare se c'è già quell'istanza. E voglio che venga aggiornato parzialmente, quindi c'è anche quello. –

+0

Hm - Devo averlo interpretato in modo errato. Vedo dai commenti dell'articolo che non sono da solo :) Forse questa [risposta SO precedente] (http://stackoverflow.com/a/18243587/308204) può aiutarti allora? – LaundroMat

risposta

5

risposta inviato da @Nirri mi ha aiutato pure, ma ho trovato la soluzione più elegante utilizzando Django QuerySet API scorciatoia:

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 

fa esattamente la stessa cosa - se Answer a quella Question non lo fa esiste, verrà creato, altrimenti restituito come è dalla ricerca di campo question.

Questo collegamento, tuttavia, non aggiornerà l'oggetto. L'API QuerySet ha un altro metodo per un'operazione update, che viene chiamata update_or_create e pubblicata in other answer down the thread.

+0

La mente fornisce un commento sul downvote? – Nevertheless

+0

Come indicato nella risposta di K Moe, questo non aggiorna l'oggetto con i nuovi valori passati. https://stackoverflow.com/a/43393253/238166 – Inti

+1

@Inti: certo che non è così. Segue un approccio REST più tradizionale e funge da riferimento per una soluzione più pulita utilizzando scorciatoie che sono ** parte dell'API che ho pubblicato un link a **. Ovviamente aggiornerò la risposta per evitare incomprensioni. – Nevertheless

3

Vorrei utilizzare il serializzatori creare metodo.

In esso è possibile verificare se la domanda (con l'ID che si fornisce nel campo relativo alla chiave primaria "domanda") ha già una risposta, e se lo fa, preleva l'oggetto e lo aggiorna, altrimenti crea un nuovo.

Quindi la prima opzione sarebbe andato qualcosa di simile:

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 

Seconda opzione sarebbe quello di controllare se la risposta con la risposta id esiste.

ID risposta di non avrebbero presentarsi nei dati convalidati delle richieste POST, a meno che non è stato utilizzato una sorta di soluzione e le ha definite manualmente come READ_ONLY = falsi campi:

id = serializers.IntegerField(read_only=False) 

Ma comunque si dovrebbe ripensare questo attraverso , C'è una buona ragione per cui il metodo PUT ei metodi POST esistono come entità separate, e dovresti separare le richieste sul frontend.

+3

Grazie mille. La prima opzione sarebbe sufficiente. Il motivo per cui voglio separarli è questo: se c'è una risposta, chiamo UpdateAnswer, altrimenti CreateAnswer sul lato anteriore, è in React btw. Quindi immaginiamo che tu e io abbiamo aperto il sito web in diversi browser. C'è una domanda senza risposta e hai fatto clic sul pulsante "SÌ" e hai inviato i dati in modo che il server crei l'oggetto. Non ho aggiornato il browser e ho fatto clic su "SÌ" o "NO", quindi invierà un'altra richiesta di creazione che fallirà. Quindi devo gestirlo sul lato server. Ecco perché. –

10

Sfortunatamente la risposta fornita e accettata non risponde alla domanda originale, poiché non aggiorna il modello.Questo però è facilmente ottenibile con un altro metodo comodo: 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 

Questo dovrebbe creare un oggetto Answer nel database se uno con question=validated_data['question'] non esiste con la risposta preso da validated_data['answer']. Se esiste già, django imposterà il suo attributo di risposta a validated_data['answer'].

Come notato dalla risposta di Nirri, questa funzione deve risiedere all'interno del serializzatore. Se si utilizza il ListCreateView generico, chiamerà la funzione di creazione una volta inviata una richiesta di posta e genererà la risposta corrispondente.

Problemi correlati