Django Rest Framework - How to insert multiple fields into serializer?

I have several basic models with several control fields. Among them, the location fields are from lat, lon, accuracy, provider and client time. Most of my writable models (and therefore resources) inherit from this base model.

I am trying to make DRF serialize the fields associated with a location in a nested "location" field. For example,

{ "id": 1, "name": "Some name", "location": { "lat": 35.234234, "lon": 35.234234, "provider": "network", "accuracy": 9.4, } } 

It is important to remember that these fields are regular (flat) fields of the base model.

I researched and found some options

  • Create a custom field and, overriding "get_attribute", create a subview. I do not like this solution because I lose some of the benefits of the model serializer, such as validation.

  • Create a sub resource called Location. I think I could make it work by adding a property with the same name on the model, but again, no checks.

So my question is: What is the best way to nest (or group) multiple fields in a DRF serializer?

DRF 3.0.0, Django 1.7

EDIT:

Building on top of @Tom Christie answer, here's what I came up with (simplified)

 # models.py class BaseModel(models.Model): id = models.AutoField(primary_key=True) lat = models.FloatField(blank=True, null=True) lon = models.FloatField(blank=True, null=True) location_time = models.DateTimeField(blank=True, null=True) location_accuracy = models.FloatField(blank=True, null=True) location_provider = models.CharField(max_length=50, blank=True, null=True) @property def location(self): return { 'lat': self.lat, 'lon': self.lon, 'location_time': self.location_time, 'location_accuracy': self.location_accuracy, 'location_provider': self.location_provider } class ChildModel(BaseModel): name = models.CharField(max_lengtg=10) # serializers.py class LocationSerializer(serializers.Serializer): lat = serializers.FloatField(allow_null=True, required=False) lon = serializers.FloatField(allow_null=True, required=False) location_time = serializers.DateTimeField(allow_null=True, required=False) location_accuracy = serializers.FloatField(allow_null=True, required=False) location_provider = serializers.CharField(max_length=50,allow_null=True, required=False) class BaseSerializer(serializers.ModelSerializer): def create(self,validated_data): validated_data.update(validated_data.pop('location',{})) return super(BaseSerializer,self).create(validated_data) def update(self, instance, validated_data): location = LocationSerializer(data=validated_data.pop('location',{}), partial=True) if location.is_valid(): for attr,value in location.validated_data.iteritems(): setattr(instance,attr,value) return super(BaseSerializer,self).update(instance, validated_data) class ChildSerializer(BaseSerializer): location = LocationSerializer() class meta: model = ChildModel fields = ('name','location',) 

I tested with a valid / invalid message / patch and it worked perfectly.

Thanks.

+7
django django-rest-framework
source share
1 answer

I would suggest just using explicit serializer classes and writing fields explicitly. It is a little more detailed, but simple, straightforward and convenient.

 class LocationSerializer(serializers.Serializer): lat = serializers.FloatField() lon = serializers.FloatField() provider = serializers.CharField(max_length=100) accuracy = serializers.DecimalField(max_digits=3, decimal_places=1) class FeatureSerializer(serializers.Serializer): name = serializers.CharField(max_length=100) location = LocationSerializer() def create(self, validated_data): return Feature.objects.create( name=validated_data['name'], lat=validated_data['location']['lat'], lon=validated_data['location']['lat'], provider=validated_data['location']['provider'], accuracy=validated_data['location']['accuracy'] ) def update(self, instance, validated_data): instance.name = validated_data['name'] instance.lat = validated_data['location']['lat'] instance.lon = validated_data['location']['lat'] instance.provider = validated_data['location']['provider'] instance.accuracy = validated_data['location']['accuracy'] instance.save() return instance 

There are several ways to use ModelSerializer or ways to keep the create and update methods a little shorter, but it is not clear that the additional indirectness you give yourself is at all worth it.

We almost always use the fully explicit serializer classes for the APIs we create.

+9
source share

All Articles