Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework - mapping serializers fields to a database column name

I am dealing with Django Rest Framework project and the generic response for my view is not what the app client expects.

The app client expects that the filed of the related models, appears as they are in the database. Example given: model City has a foreign key to Country model, represented by a country_id column.

Is there any option to "map" the serializers default fields into a custom one? I have check the Django Rest Framework documentation but I only found "serializer_field_mapping" but I don't know if it will fit my requirements and also I don't know how to use it.

Somehow I got a close approach of it, but only in the case for fetching data --creating / updating threw some errors that I did not get how to manage. :(

Bellow I attach my models.py file, plus the actual output and the desired output. Also, if it is possible, I would like to retrieve data related with Country / Region if exists combined with the database column field_id names.

Thanks in advance,

models.py

from django.db import models
from django.contrib.postgres.fields import ArrayField


class Country(models.Model):

    name = models.CharField(max_length=150, unique=True, blank=False)

    class Meta:
        db_table = 'countries'


class Region(models.Model):

    name = models.CharField(max_length=150, unique=True, blank=False)
    code = models.CharField(max_length=150, blank=True)

    class Meta:
        db_table = 'regions'


class City(models.Model):

    country = models.ForeignKey(Country)
    region = models.ForeignKey(Region, null=True, blank=True, default=None)
    name = models.CharField(max_length=150, unique=True, blank=False)
    postal_codes = ArrayField(models.CharField(max_length=12, blank=True), null=True, blank=True, default=None)

    def __str__(self):
        if not self.region:
            return '%s (%s)' % (self.name, self.country.name)
        return '%s, %s (%s)' % (self.name, self.region.name, self.country.name)

    class Meta:
        db_table = 'cities'

Actual Output:

[
  {
    "id": 1,
    "name": "San Francisco",
    "postal_codes": null,
    "country": 1,
    "region": 1
  },
  {
    "id": 2,
    "name": "Palo Alto",
    "postal_codes": null,
    "country": 1,
    "region": 1
  },
  {
    "id": 3,
    "name": "New York City",
    "postal_codes": null,
    "country": 1,
    "region": 2
  },
  {
    "id": 4,
    "name": "London",
    "postal_codes": null,
    "country": 2,
    "region": null
  }
]

Desired Output:

[
  {
    "id": 1,
    "country_id": 1,
    "region_id": 1,
    "name": "San Francisco",
    "postal_codes": null
  },
  {
    "id": 2,
    "country_id": 1,
    "region_id": 1,
    "name": "Palo Alto",
    "postal_codes": null
  },
  {
    "id": 3,
    "country_id": 1,
    "region_id": 2,
    "name": "New York City",
    "postal_codes": null
  },
  {
    "id": 4,
    "country_id": 2,
    "region_id": null,
    "name": "London",
    "postal_codes": null
  }
]
like image 815
jordimarinvalle Avatar asked Sep 28 '15 19:09

jordimarinvalle


2 Answers

You can use the source parameter of a field on your serializer to achieve this. For example:

from rest_framework import serializers

from .models import City


class CitySerializer(serializers.ModelSerializer):
    country_id = serializers.IntegerField(source='country')
    region_id = serializers.IntegerField(source='region')

    class Meta:
        model = City
        fields = ('id', 'country_id', 'region_id', 'name', 'postal_codes')

EDIT: As Yaroslav pointed out, when doing it in this way, you don't need to include the source. Take note, however, that simply including country_id or region_id in the fields list is not sufficient. You still need to specify the field on the serializer, such as country_id = serializers.IntegerField() and also include it in the fields.

like image 128
Joey Wilhelm Avatar answered Oct 23 '22 15:10

Joey Wilhelm


Thanks to all. Finally I got it. See code bellow. I answer my question due to there is no option to add large amount of lines on a answer comment.

serializers.py

from rest_framework import serializers
from .models import Country, Region, City


class CountrySerializer(serializers.ModelSerializer):

    class Meta:
        model = Country
        fields = ('id', 'name')


class RegionSerializer(serializers.ModelSerializer):

    class Meta:
        model = Region
        fields = ('id', 'name', 'code')


class CitySerializer(serializers.ModelSerializer):

    country_id = serializers.PrimaryKeyRelatedField(
        queryset=Country.objects.all(),
        required=True,
        source='country',
    )
    region_id = serializers.PrimaryKeyRelatedField(
        queryset=Region.objects.all(),
        allow_null=True,
        required=False,
        source='region',
    )

    country = CountrySerializer(
        read_only=False,
        required=False,
    )
    region = RegionSerializer(
        required=False,
        allow_null=True,
        read_only=True,
    )

    class Meta:
        model = City
        fields = (
            'id',
            'country', 'region',
            'name', 'postal_codes',
            'country_id', 'region_id',
        )
like image 8
jordimarinvalle Avatar answered Oct 23 '22 17:10

jordimarinvalle