2015-10-29 16 views
6

Sono novizio in python e resto django. Ma sono confuso. Qual è il modo migliore per aggiornare la relazione molti a molti nel framework django rest. Ho letto i documenti http://www.django-rest-framework.org/api-guide/relations/#manytomanyfields-with-a-through-model Per impostazione predefinita, i campi relazionali che hanno come target un ManyToManyField con un modello passante specificato sono impostati in sola lettura.Django Rest update molti a molti di id

Se si specifica esplicitamente un campo relazionale che punta a un ManyToManyField con un modello passante, assicurarsi di impostare read_only su True.

Quindi, se ho un codice

class Master(models.Model): 
    # other fields 
    skills = models.ManyToManyField(Skill) 

class MasterSerializer(serializers.ModelSerializer): 
    skills = SkillSerializer(many=True, read_only=False) 

Ciò restituirà abilità come elenco di oggetti. E non ho un modo per aggiornarli. Per quanto ho capito, Django preferisce lavorare con gli oggetti rispetto all'id oggetto quando si tratta di M2M. Se lavoro con yii o rails lavorerò con i modelli "through". Vorrei ottenere il campo skill_ids. Che potessi leggere e scrivere. E posso farlo per l'operazione di scrittura

class MasterSerializer(serializers.ModelSerializer): 
    skill_ids = serializers.ListField(write_only=True) 

    def update(self, instance, validated_data): 

    # ... 
    validated_data['skill_ids'] = filter(None, validated_data['skill_ids']) 
    for skill_id in validated_data['skill_ids']: 
     skill = Skill.objects.get(pk=skill_id) 
     instance.skills.add(skill) 

    return instance 

Ma non riesco a farlo tornare skill_ids in campo. E lavoro per operazioni di lettura e scrittura.

+0

Non è chiaro su ciò che si sta cercando di fare. Potresti aggiungere un esempio JSON di cosa vuoi inserire? – Linovia

+0

{id: 123, first_name: "John", "skill_ids": [1, 2, 3]} – radzserg

+0

Lo scenario comune: si ottiene un elenco di competenze e si opera con ID non su oggetti. Non ha senso inviare l'oggetto completo per aggiornare le relazioni. { "first_name": "John", "competenze": [ {id: 1, nome: "Nome 1"}, {id: 2, il nome: "Nome 2"}, ] } – radzserg

risposta

11

Alcune cose da notare.

Per prima cosa, non si esegue una tabella esplicita tramite il proprio esempio. Quindi puoi saltare quella parte.

In secondo luogo si sta tentando di utilizzare serializzatori nidificati che sono molto più complessi di quello che si sta tentando di ottenere.

Si può semplicemente lettura/scrittura correlata id utilizzando un PrimaryKeyRelatedField:

class MasterSerializer(serializers.ModelSerializer): 
    skills_ids = serializers.PrimaryKeyRelatedField(many=True, read_only=False, queryset=Skill.objects.all(), source='skills') 

Che dovrebbe essere in grado di lettura/scrittura:

{id: 123, first_name: "John", "skill_ids": [1, 2, 3]} 

Si noti che la mappatura da "skill_ids" di JSON per le "abilità" del modello vengono eseguite utilizzando l'argomento facoltativo source

+0

Grazie, questo è quello di cui avevo bisogno. – radzserg

+0

E se il design indica: "Scegli esistente o aggiungi uno nuovo". Dovremmo usare sempre serializzatori nidificati altrimenti? – Feanor

+0

In tal caso, è possibile creare prima l'istanza correlata e quindi aggiungerla all'elenco o ottenere con serializzatori nidificati. – Linovia

1

Proverò a fare un po 'di luce in termini di design: in Django se si specifica il modello per ManyToManyRelation, il campo di relazione sul modello diventa di sola lettura. Se è necessario modificare le associazioni, lo si fa direttamente sul modello passante, eliminando o registrando nuovi record.

Ciò significa che potrebbe essere necessario utilizzare un serializzatore completamente diverso per il modello passante o scrivere metodi di aggiornamento/creazione personalizzati.

Ci sono alcuni set di nuovo con modello personalizzato, sei sicuro di non essere abbastanza bravo con l'implementazione predefinita di ManyToManyFields?

+0

Beh, sono interessato all'implementazione del django-rest. E proprio ora non vedo come aggiornare le relazioni m2m con gli strumenti integrati. – radzserg

0

Ho affrontato questo problema per un po 'di tempo e ho trovato che il modo migliore per risolvere il problema generale l'aggiornamento di molti a molti campi sta lavorando intorno ad esso.

Nel mio caso esiste un modello chiamato Listing e un utente può effettuare un abbonamento (l'altro modello) a un'istanza del modello di elenco. L'abbonamento funziona con una chiave esterna generica e l'elenco importa le iscrizioni degli utenti tramite Many2Many.

Invece di effettuare una richiesta PUT al modello di elenco tramite API, aggiungo semplicemente l'istanza di sottoscrizione al modello corretto nel metodo POST della vista API di Subscription. Ecco il mio codice rettificato:

#Model 
    class Listing(models.Model): 

     #Basics 
     user = models.ForeignKey(settings.AUTH_USER_MODEL) 
     slug = models.SlugField(unique=True, blank=True) 
     timestamp = models.DateTimeField(auto_now_add=True, auto_now=False) 

     #Listing 
     title = models.CharField(max_length=200) 
     price = models.CharField(max_length=50, null=True, blank=True) 
     subscriptions = models.ManyToManyField(Subscription, blank=True) 

    class Subscription(models.Model): 
     user = models.ForeignKey(settings.AUTH_USER_MODEL) 
     content_type = models.ForeignKey(ContentType) 
     object_id = models.PositiveIntegerField() 
     content_object = GenericForeignKey('content_type', 'object_id') 
     timestamp = models.DateTimeField(auto_now_add=True) 

    #Views 
    class APISubscriptionCreateView(APIView): #Retrieve Detail 

     def post(self, request, format=None): 
     serializer = SubscriptionCreateSerializer(data=request.data) 
     if serializer.is_valid(): 
      sub = serializer.save(user=self.request.user) 
      object_id = request.data['object_id'] 
      lis = Listing.objects.get(pk=object_id) 
      lis.subscriptions.add(sub) 

      return Response(serializer.data, status=status.HTTP_201_CREATED) 
     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

Spero che questo vi aiuterà, mi c'è voluto un po 'per capirlo