Как я могу соединить две несвязанные модели Django на расстоянии?

Я работаю над проектом Django, который требует, чтобы я сообщал через веб-службу для каждого элемента в школьной таблице все больницы, которые случайно оказались на заданном расстоянии. Вот фиктивная реализация двух моделей:

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()

Я заявлю для протокола, что это надуманный пример, но суть остается в том, что:

  1. Ни одна из моделей не имеет отношений внешнего ключа с другой (и не должна)
  2. Единственные отношения через расстояние

Теперь я использую Django Rest Framework (DRF) для обработки своих запросов, и я использую классы Serializer, которые он предоставляет, чтобы сделать что-то в этом роде:

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

Это работает, но не эффективно. По сути, DRF загружает все школы, а затем перебирает каждую школу и выполняет запрос в get_nearby_hospitals. Количество запросов к базе данных равно количеству предметов в школах плюс один (чтобы получить школы).

В идеале я бы хотел, чтобы решение Django выполняло что-то в этом духе (очевидно, с перечисленными полями, псевдонимами для конфликтующих имен столбцов идентификаторов и т. д.):

SELECT * FROM schools JOIN 
hospitals ON ST_DWithin(schools.location, hospitals.location, 10000)

Приведенный выше запрос выдает пересечение всех школ и больниц на нужном расстоянии. Я мог бы вручную объединить эти результаты с набором запросов School.objects.all() или написать лучший запрос и вызвать метод raw QuerySet, чтобы получить то, что мне нужно, одним выстрелом.

Есть ли лучшее или более «Django Way» решение этой проблемы?


person ZachS    schedule 27.06.2013    source источник
comment
Как вы считали количество запросов? Какое представление Django Rest Framewok вы используете?   -  person Benjamin Toueg    schedule 27.06.2013
comment
Я подсчитал количество запросов с помощью панели инструментов Django Debug. Рассматриваемое представление представляет собой DRF ListCreateAPIView.   -  person ZachS    schedule 27.06.2013
comment
Убедитесь, что вы тестируете json-версию страницы, а не HTML. На самом деле создание формы для представления Create API в HTML может привести к множеству запросов, которые обычно не нужны.   -  person Benjamin Toueg    schedule 27.06.2013


Ответы (1)


Я не уверен, что это лучше, но я думаю, что вы решаете проблему в обратном направлении. Я вообще не вижу причин для какого-либо JOIN.

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:
            # why do you query School model if you want nearby hospitals?
            locations = Hospital.objects.filter(location__distance_lte=(geom ,10000))
            return locations # not sure it's the good type to return though
        except:
            return
person Benjamin Toueg    schedule 27.06.2013
comment
Это потому, что это опечатка в моем примере. Как вы сказали, строка под блоком try должна быть Hospital. Однако здесь это не решает основной проблемы. - person ZachS; 27.06.2013