I'm working on a Django project that requires me to report, via web service, for each item in the School table all Hospitals that just so happen to be a set distance away. Here is a dummy implementation of the two models:
class School(models.Model):
location = models.PointField(srid=4326, geography=True)
...some other fields...
objects = models.GeoManager()
class Hospital
location = models.PointField(srid=4326, geography=True)
...some other fields...
objects = models.GeoManager()
I will state for the record that this is a contrived example, but the point remains that:
- Neither model has a foreign key relationship with the other (nor should they)
- The only relationship is via distance
Now, I am using Django Rest Framework (DRF) to render my requests, and I am using the Serializer classes that it provides to do something along these lines:
class SchoolSerializer(serializers.ModelSerializer):
nearby_hospitals = serializers.SerializerMethodField('get_nearby_hospitals')
class Meta:
model = School
fields = ('location', 'nearby_hospitals',)
def get_nearby_hospitals(self, obj):
geom = obj.location
try:
locations = Hospitals.objects.filter(loc__dwithin=(geom, 10000))
return HospitalSerializer(locations, many=True).data
except:
return
This works but it is not efficient. In essence what happens is that DRF loads all Schools, and then from there loops over each school and runs the query in get_nearby_hospitals. The number of database queries is equal to the number of items in Schools plus one (to get the Schools).
Ideally I'd like a Django solution that performs something along these lines (obviously with the fields listed out, aliases for clashing ID column names, etc):
SELECT * FROM schools JOIN
hospitals ON ST_DWithin(schools.location, hospitals.location, 10000)
The above query produces the intersection of all Schools and Hospitals within the proper distance. I could merge these results in manually with the School.objects.all()
queryset or write a better query and call the raw
QuerySet method to get what I'm after in one shot.
Is there a better or more 'Django Way' solution to this problem?