How to post to a Django REST Framework API with Re

2019-05-06 14:50发布

问题:

I have two related models (Events + Locations) with a serialzer shown below:

class Locations
    title = models.CharField(max_length=250)
    address = model.CharField(max_length=250)

class Events
   title = models.CharField(max_length=250)
   locations = models.ForeignKey(Locations, related_name='events'

class EventsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Events
        depth = 1

I set the depth to 1 in the serializer so I can get the information from the Locations model instead of a single id. When doing this however, I cant post to events with the location info. I can only perform a post with the title attribute. If I remove the depth option in the serializer, I can perform the post with both the title and location id.

I tried to create a second serializer (EventsSerialzerB) without the depth field with the intention of using the first one as a read-only response, however when I created a second serializer, viewset, and added it to the router, it would automatically override the original viewset.

Is it possible for me to create a serializer that outputs the related model fields, and allows you to post directly to the single model?

// EDIT - Here's what I'm trying to post

$scope.doClick = function (event) {

    var test_data  = {
        title: 'Event Test',
        content: 'Some test content here',
        location: 2,
        date: '2014-12-16T11:00:00Z'
    }

    // $resource.save() doesn't work?
    $http.post('/api/events/', test_data).
        success(function(data, status, headers, config) {
            console.log('sucess', status);
        }).
        error(function(data, status, headers, config) {
            console.log('error', status);
        });
}

So when the serializers are flat, I can post all of these fields. The location field is the id of a location from the related Locations table. When they are nested, I can't include the location field in the test data.

回答1:

By setting the depth option on the serializer, you are telling it to make any relation nested instead of flat. For the most part, nested serializers should be considered read-only by default, as they are buggy in Django REST Framework 2.4 and there are better ways to handle them in 3.0.

It sounds like you want a nested representation when reading, but a flat representation when writing. While this isn't recommended, as it means GET requests don't match PUT requests, it is possible to do this in a way to makes everyone happy.

In Django REST Framework 3.0, you can try the following to get what you want:

class LocationsSerializer(serializers.ModelSerializer):

    class Meta:
        model = Locations
        fields = ('title', 'address', )

class EventsSerializer(serializers.ModelSerializer):
    locations = LocationsSerializer(read_only=True)

    class Meta:
        model = Events
        fields = ('locations', )

class EventViewSet(viewsets.ModelViewSet):
    queryet = Event.objects.all()
    serializer_class = EventsSerializer

    def perform_create(self, serializer):
        serializer.save(locations=self.request.data['locations'])

    def perform_update(self, serializer):
        serializer.save(locations=self.request.data['locations'])

A new LocationsSerializer was created, which will handle the read-only nested representation of the Locations object. By overriding perform_create and perform_update, we can pass in the location id that was passed in with the request body, so the location can still be updated.

Also, you should avoid having model names being plurals. It's confusing when Events.locations is a single location, even though Locations.events is a list of events for the location. Event.location and Location.events reads a bit more clearly, the Django admin will display them reasonably, and your fellow developers will be able to easily understand how the relations are set up.