I have several a base model with several control fields. Among them a location fields compound from lat, lon, accuracy, provider and client time. Most of my writable models (and hence resources) are inheriting from this base model.
I'm trying to make DRF serialize the location related fields in a nested "location" field. For example,
{
"id": 1,
"name": "Some name",
"location": {
"lat": 35.234234,
"lon": 35.234234,
"provider": "network",
"accuracy": 9.4,
}
}
I'ts important to remember that these fields are regular (flat) fields on the base model.
I've investigated and found several options
Create a custom field and by overriding "get_attribute" create the nested representation. I don't like this solution because i lose some of the benefits of the model serializer such as validation.
Create a nested resource called Location. I guess i could make it work by adding a property by the same name on the model but again, no validations.
So my question is, What is the best way to nest ( or group ) several fields in a DRF serializer ?
DRF 3.0.0, Django 1.7
EDIT:
Building on top of @Tom Christie answer this is 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've tested with valid/invalid post/patch and it worked perfectly.
Thanks.