Как я могу выполнить этот сложный SQL-запрос с помощью Django ORM? (подзапрос с соединением)

Я привык писать свои собственные SQL-запросы и пытаюсь привыкнуть ко всей ORM-системе, которая сейчас так популярна.

Вот запрос:

SELECT * FROM routes WHERE route_id IN (
    SELECT DISTINCT t.route_id FROM stop_times AS st 
    LEFT JOIN trips AS t ON st.trip_id=t.trip_id
    WHERE stop_id = %s
)

где %s — целое число.

Я использую ORM Django по умолчанию. Какой самый питонический способ сделать это?

Некоторая справочная информация: БД, которую я использую, взята из GTFS (спецификация фида Google Transit). Этот запрос должен получить список всех route, которые проходят через определенный stop, однако информация, связывающая их, находится в таблице trips.

Этот запрос отлично работает для меня, поэтому единственная причина, по которой я спрашиваю, - это учиться.

Спасибо!


person Murat Ayfer    schedule 28.10.2010    source источник


Ответы (2)


Вероятно, было бы немного проще найти подходящий способ сделать это, если бы у вас было то, что вы использовали для соответствующего Models.

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

class Route(models.Model):
    #bunch of stuff
    pass
class Stop(models.Model):
    #bunch of stuff
    stop_times = models.ManyToManyField(through=StopTime)
class StopTime(models.Model):
    trip = models.ForeignKey(Trip)
    stop = models.ForeignKey(Stop)
    # bunch of additional meta about this M2M table
    pass
class Trip(models.Model):
    route = models.ForeignKey(Route)
    # bunch of stuff

Если это так... вы сможете сделать что-то вроде

Route.objects.filter(trip__stop__id=my_stop_id)

чтобы получить все объекты Route, которые проходят через заданный Stop, с первичным ключом id, равным my_stop_id, который, как я предполагаю, является целым числом согласно вашему сообщению.

Я извиняюсь, если синтаксис немного неправильный, так как мне не нужно было делать отношения «многие ко многим», используя явную дополнительную таблицу. Некоторая корректировка может также потребоваться, если вы должны (или хотите) использовать параметр related_name для любых внешних ключей или поля «многие ко многим».

person desfido    schedule 29.10.2010
comment
Большое спасибо, это очень полезно. Вы правильно поняли модели, и я скоро попробую этот запрос. Еще один вопрос: в чем разница между ForeignKey и ManyToMany? - person Murat Ayfer; 29.10.2010
comment
ForeignKeys предназначены для отношений «многие к одному» и помещаются в Model на стороне «Многие». ManyToManyFields предназначены для отношений «многие ко многим»; на самом деле не так уж важно, в какую из двух связанных моделей он вставлен, поэтому вы обычно помещаете его в тот, где вы, скорее всего, захотите его отредактировать (например, встроенные формы администратора Django) . Таблица автоматически создается для представления отношений, которые в основном состоят из двух ForeignKey объектов. - person desfido; 29.10.2010
comment
(о, но в этом случае, поскольку есть дополнительные метаданные об отношениях «многие ко многим», я вместо этого предположил, что вы будете делать это вручную, через StopTime. Вот почему параметр through необходимо использовать для ManyToManyField. Если вы решили, что дополнительные данные, данные для StopTime, были посторонними, кроме информации «многие ко многим», тогда вы можете исключить модель StopTime и параметр through.Обычные соглашения об именах Django предполагают, что вы называете ManyToManyField как times или что-то подобное, затем .) - person desfido; 29.10.2010

Поправьте меня, если я ошибаюсь, но я не думаю, что вы можете сделать это с Django ORM обычным способом.

Нет поддержки подзапросов, и при обычном соединении это будет зависеть от вашей базы данных, может ли вам помочь отдельный запрос. Если вы используете Postgres, вы можете сделать это с помощью этого патча: http://code.djangoproject.com/ticket/6422

Запрос будет примерно таким:

Route.objects.filter(stop_time__trips__stop_id=...).distinct('stop_time__route_id')
person Wolph    schedule 28.10.2010