2013-03-04 9 views
8

Sto cercando di esporre un'API al mio modello Django tramite il framework Rango di Django.Registrazione di una relazione uno-a-molti

Ho un oggetto Observation. Un'osservazione può contenere più cose che sono state osservate. Così l'ho rappresentato così:

class Observation(models.Model): 

    photo_file = models.ImageField(upload_to=img_dir, blank=True, null=True) 
    titestamp = models.DateTimeField(blank=True, null=True) 
    latitude = models.FloatField() 
    longitude = models.FloatField() 


class ObservedThing(models.Model): 
    thing = models.ForeignKey(Thing) # the thing being observed 
    observation = models.ForeignKey(Observation, related_name='observed_thing') 
    value = models.FloatField() 

Come ho capito, questa è una relazione uno-a-molti.

Ora ho un'API Vista:

class ObsvList(generics.ListCreateAPIView): 
    """ 
    API endpoint that represents a list of observations. 
    """ 
    model = Observation 
    serializer_class = ObsvSerializer 

e la corrispondente serialiser:

class ObsvSerializer(serializers.ModelSerializer): 

    observed_thing = serializers.PrimaryKeyRelatedField(many=True) 

    class Meta: 
     model = Observation 

Che cosa devo fare per essere in grado di pubblicare un'osservazione con diverse cose rilevato? Io non riesco a capire. Grazie molto.

risposta

8

(rispondere più o meno copiato da un'altra similar but less clear question)

Per creare più oggetti correlati in un singolo POST richiede scrivibili serializzatori nidificati che non sono ancora disponibili.

Pieno sostegno è un work in progress, ma nel frattempo una soluzione (hacky) è quello di sovrascrivere il metodo create nella vista in ogni caso:

class FooListCreateView(ListCreateAPIView): 
    model = Foo 
    serializer_class = FooSerializer 

    def create(self, request, *args, **kwargs): 
     data=request.DATA 

     f = Foo.objects.create() 

     # ... create nested objects from request data ... 

     # ... 
     return Response(serializer.data, 
         status=status.HTTP_201_CREATED, 
         headers=headers) 

Probabilmente non è l'ideale, ma funziona per me fino a il modo giusto arriva.

L'altra opzione consiste nel creare i relativi oggetti Observation singolarmente con POST separati e utilizzare PrimaryKeyRelatedField or HyperlinkedRelatedField per creare le associazioni nel POST finale ObservedThing.

+0

Aha, questo è quello che stavo cercando. Molte grazie, risposta accettata. – gozzilli

1
thing = models.ManyToManyField('Thing') 

è necessario utilizzare relazione molti a molti per la creazione di una tabella temporanea che memorizzerà automaticamente le chiavi e dei dati associati.

+0

Grazie, ma la mia tabella intermedia è quella di "ObservedThing''. '' ManyToManyField'' funziona solo se non hai altre informazioni associate alla relazione. Quello che posso fare è avere '' thing = models.ManyToManyField (Thing, through = 'ObservedThing') '', ma ciò non risolve il mio problema originale. – gozzilli

4

So che questo thread ha già una risposta, ma ho iniziato a lavorare per risolvere questo problema, e poiché questo post è stato una delle mie ispirazioni, vorrei condividere la mia soluzione finale. Può essere utile a qualcuno.Ho i modelli, quindi la classe padre:

#parent model class 
class Parent(models.Model): 

    id = models.AutoField(primary_key=True) 
    field = models.CharField(max_length=45) 

    class Meta: 
     managed = False 
     db_table = 'parent' 

allora, la classe figlio:

#child model class 
class Child(models.Model): 

    id = models.AutoField(primary_key=True) 
    field = models.CharField(max_length=45) 
    parent = models.ForeignKey(Parent, related_name='children') 

    class Meta: 
     managed = False 
     db_table = 'child' 

dovessi definire i serializzatori, dal momento che non volevo creare un router URL accessibile a gestire direttamente gli oggetti per bambini, ma ho voluto creare loro attraverso la ModelViewSet del genitore ModelViewSet, questo è quello che mi serviva:

class ChildSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Child 
     read_only_fields = ('id',) 

class ParentSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Banner 
     read_only_fields = ('id',) 

class ParentSerializerNested(ParentSerializer): 
    children = ChildSerializer(many=True) 

ero quindi pronto per creare il ModelViewSet, sovrascrivendo/ex tendente alla creazione di mixins/aggiornamento, e renderlo generico al fine di riutilizzare per altri casi:

class ParentChildViewSet(viewsets.ModelViewSet): 

    def create(self, request, *args, **kwargs): 
     serializer = self.serializer_parent(data=request.DATA, 
              files=request.FILES) 

     try: 
      if serializer.is_valid(): 
       with transaction.commit_on_success(): 
        self.pre_save(serializer.object) 
        parent = serializer.save(force_insert=True) 
        self.post_save(parent, created=True) 

        # need to insert children records 
        for child in request.DATA[self.child_field]: 
         child[self.parent_field] = parent.id 
         child_record = self.serializer_child(data=child) 
         if child_record.is_valid(): 
          child_record.save(force_insert=True) 
         else: 
          raise ValidationError('Child validation failed') 

        headers = self.get_success_headers(serializer.data) 

        serializer.data[self.child_field] = self.serializer_child(
         self.model_child.objects.filter(
          **{self.parent_field: parent.id}).all(), 
          many=True).data 
        return Response(serializer.data, 
            status=status.HTTP_201_CREATED, 
            headers=headers) 
     except ValidationError: 
      pass 
     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

Così possibile riutilizzarlo per ogni caso relazione nidificato ho nel mio app come questo:

class ParentViewSet(ParentChildViewSet): 
    child_field = 'children' 
    parent_field = 'parent' 
    model = Parent 
    model_child = Child 
    serializer_class = ParentSerializerNested 
    serializer_parent = ParentSerializer 
    serializer_child = ChildSerializer 

E alla fine, il percorso:

router = routers.DefaultRouter() 
router.register(r'parents', ParentViewSet) 

funziona come un fascino!

Problemi correlati