Title: select_related in view is not reducing queries with hybrid property in Django
Tags: python-3.x,django,django-models,django-rest-framework,django-views

Question: I have included select_related and overall is reducing the number of queries and prefetching data as expected. However, I have a case which is querying separately. I am not sure if I am expecting a behaviour that would not be possible.
I have the following view:
list.py (View) where I am using select_related:
class OpportunitiesList(generics.ListAPIView):
    serializer_class = OpportunityGetSerializer

    def get_queryset(self):
        queryset = Opportunity.objects.all()
        queryset = queryset.filter(deleted=False)

        return queryset.select_related('nearest_airport').order_by('id')

I have four objects in the models:
opportunity.py (Model):
class Opportunity(models.Model):
    deleted = models.BooleanField(
        default=False,
    )
    opportunity_name = models.TextField(blank=True, null=True)
    nearest_airport = models.ForeignKey(AirportDistance,
                                        on_delete=models.SET_NULL,
                                        db_column="nearest_airport",
                                        null=True)
class AirportDistance(models.Model):
    airport_id = models.ForeignKey(Airport,
                                   on_delete=models.SET_NULL,
                                   db_column="airport_id",
                                   null=True)
    airport_distance = models.DecimalField(max_digits=16, decimal_places=4, blank=False, null=False)

    @property
    def airport_name(self):
        return self.airport_id.name

location_assets.py (Model):
class Location(models.Model):
    name = models.CharField(null=True, blank=True, max_length=255)
    location_fuzzy = models.BooleanField(
        default=False,
        help_text=
        "This field should be set to True if the `point` field was not present in the original dataset "
        "and is inferred or approximated by other fields.",
    )
    location_type = models.CharField(null=True, blank=True, max_length=255)
    
    
class Airport(Location):
    description = models.CharField(null=True, blank=True, max_length=255)
    aerodrome_status = models.CharField(null=True, blank=True, max_length=255)
    aircraft_access_ind = models.CharField(null=True, blank=True, max_length=255)
    data_source = models.CharField(null=True, blank=True, max_length=255)
    data_source_year = models.CharField(null=True, blank=True, max_length=255)

    class Meta:
        ordering = ("id", )

Lastly, I have the serializers to transform the data:
get.py: (Serializer)
class OpportunityGetSerializer(serializers.ModelSerializer):
    nearest_airport = OpportunityAirportSerializer(required=False)
    
    class Meta:
        model = Opportunity
        fields = (
            "id",
            "opportunity_name",
            "nearest_airport",
        )

distance.py (Serializer)
class OpportunityAirportSerializer(serializers.ModelSerializer):
    class Meta:
        model = AirportDistance
        fields = ('airport_id', 'airport_distance','airport_name')

The above is creating two queries instead of one:
SELECT opportunity.id,
       opportunity.opportunity_name,
       opportunity.nearest_airport,
       airportdistance.id,
       airportdistance.airport_id,
       airportdistance.airport_distance
  FROM opportunity
  LEFT OUTER JOIN airportdistance
    ON (opportunity.nearest_airport = airportdistance.id)
 WHERE opportunity.deleted = false
 ORDER BY opportunity.id ASC

SELECT location.name
  FROM airport
 INNER JOIN location
    ON (airport.location_ptr_id = location.id)
 WHERE airport.location_ptr_id = 1

AirportDistance model  is connected to Airport model which in turn is connected to Location model. In AirportDistance, I included a hybrid property to retrieve the name from Airport that is coming from Location model. I am not sure if the hybrid property in here would affect select_related.
Is possible just to get the following query and mix both queries:
SELECT opportunity.id,
       opportunity.opportunity_name,
       opportunity.nearest_airport,
       airportdistance.id,
       airportdistance.airport_id,
       airportdistance.airport_distance,
       location."name" 
      FROM opportunity
      LEFT OUTER JOIN airportdistance
        ON (opportunity.nearest_airport = airportdistance.id)
      INNER join airport 
        ON (airport.location_ptr_id = airportdistance.airport_id)
      INNER join location
        ON (location.id = airport.location_ptr_id)
     WHERE opportunity.deleted = false
     ORDER BY opportunity.id ASC

Answer: you need to do select_related('nearest_airport__airport_id') instead of select_related('nearest_airport')
since AirportDistance accesses airport_id.name when resolving airport_name.