2013-03-04 10 views
8

Ich versuche, eine API für mein Django-Modell über das Django-REST-Framework bereitzustellen.Eine 1: n-Beziehung schreiben

Ich habe ein Objekt Observation. Eine Beobachtung kann mehrere Dinge enthalten, die beobachtet wurden. Also habe ich es so dargestellt:

Wie ich verstehe, ist dies eine Eins-zu-viele-Beziehung.

ich jetzt eine API-Ansicht haben:

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

und die entsprechenden Serializer:

class ObsvSerializer(serializers.ModelSerializer): 

    observed_thing = serializers.PrimaryKeyRelatedField(many=True) 

    class Meta: 
     model = Observation 

Was tun ich in der Lage zu tun haben, zu erfassende eine Beobachtung mit mehreren Dingen POST? Ich kann es nicht herausfinden. Danke vielmals.

Antwort

8

(Antwort mehr oder weniger kopiert von einer anderen similar but less clear question)

Um mehr ähnlichen Objekte in einem einzigen POST erfordert beschreibbaren verschachtelten Serializer, die noch nicht vorhanden zu erstellen.

Volle Unterstützung ist ein work in progress, aber in der Zwischenzeit eine (hacky) Lösung ist, die create Verfahren in der Ansicht jeweils außer Kraft zu setzen:

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) 

Wahrscheinlich nicht ideal, aber es funktioniert für mich, bis Der richtige Weg kommt. Die andere Option besteht darin, die zugehörigen Observation Objekte einzeln mit separaten POSTs zu erstellen, und die PrimaryKeyRelatedField or HyperlinkedRelatedField die Assoziationen im endgültigen ObservedThing POST zu erstellen.

+0

Aha, das habe ich gesucht. Vielen Dank, Antwort akzeptiert. – gozzilli

1
thing = models.ManyToManyField('Thing') 

müssen Sie viele zu viele Beziehung verwenden, um eine temporäre Tabelle erstellen, die die Schlüssel und assoziierten Daten automatisch gespeichert werden.

+0

Danke, aber meine Zwischentabelle ist das '' ObservedThing''. Das '' ManyToManyField'' funktioniert nur, wenn Sie keine weiteren Informationen an die Beziehung angehängt haben. Was ich tun kann ist "Ding = models.ManyToManyField (Thing, through = 'ObservedThing')", aber das löst immer noch nicht mein ursprüngliches Problem. – gozzilli

4

Ich weiß, dass dieser Thread bereits eine Antwort hat, aber ich begann zu arbeiten, um dieses Problem zu lösen, und da dieser Beitrag eine meiner Inspirationen war, möchte ich meine endgültige Lösung teilen. Es kann für jemanden nützlich sein.Ich habe die Modelle, so dass die Elternklasse:

#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' 

dann, das Kind Klasse:

#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' 

ich die Serializer definieren musste, da ich nicht über einen Router erreichbar URL erstellen wollte direkt Kinder Objekte verwalten, aber ich wollte, dass sie durch die ModelViewSet der Mutter ModelViewSet schaffen, ist es das, was ich brauchte:

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) 

ich war dann bereit, die ModelViewSet, zwingende/ex zu erstellen dazu neigt, die/update Mixins, erstellen und generische machen, um sie für andere Fälle wieder zu verwenden: für

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) 

So jede verschachtelte Beziehung kann ich Fall wiederzuverwenden ich in meiner app wie dieses:

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

Und am Ende, das Routing:

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

Es wirkt wie ein Zauber!