djangodjango-rest-frameworkdjango-viewsdjango-admindjango-select-related

Django select_related Not Working as Expected


I have 2 classes, Country and Coordinates. When making an API call to the Country table, I would like the API (using DRF) to return the related Coordinate information as well.

By default, the following is understandably returned (localhost:8000/api/country/):

{
    "id": 1,
    "name": "TestCountry",
    "coordinates": 1
}

The problem is that after implementing select_related in my views.py file, and modifying the CountrySerializer as well, the output remains exactly the same. I had expected something like:

{
    "id": 1,
    "name": "TestCountry",
    "coordinates": {
        "longitude": 123,
        "latitude": 456,
    }
}

Or even this would suffice:

{
    "id": 1,
    "name": "TestCountry",
    "longitude": 123,
    "latitude": 456,
}

And here are the relevant code in models, views, and serializer files.

class Coordinates(models.Model):
    longitude = models.DecimalField()
    latitude = models.DecimalField()

class Country(models.Model):
    name = models.CharField()
    coordinatesID = models.ForeignKey(Coordinates, on_delete=models.SET_NULL, verbose_name="Coordinates", db_column="CoordinatesID", blank=True, null=True, related_name="coordinates")

class CountryViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Country.objects.select_related('coordinatesID').all()
    serializer_class = CountrySerializer

class CountrySerializer(DynamicFieldModelSerializer):
    longitude = serializers.ReadOnlyField(source='coordinates.longitude')
    latitude = serializers.ReadOnlyField(source='coordinates.latitude')

    class Meta:
        model = Country
        fields = '__all__'

Additionally, in the Country table, I specified the related_name="coordinates", however select_related does not recognize this option, and I still have to use "coordinatesID" to reference the coordinate table. Is this a bug or related to incorrect implementation?


Solution

  • Note that related_name is used to access the relationship in reverse from the Coordinates model side to Country side not the opposite.

    so in the select_related you can use all fields and relationships that are defined on the Model itself, so for Country model if you defined coordinates relationship as coordinatesID you will need to do like this

    queryset = Country.objects.select_related('coordinatesID').all()
    

    and to use in the serializer

    longitude = serializers.ReadOnlyField(source='coordinatesID.longitude')
    latitude = serializers.ReadOnlyField(source='coordinatesID.latitude')
    

    also using a field name like coordinatesID is not great naming convention in django since you would have a property like coordinatesID.id which is not very clean.

    https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_one/