Я новичок в SQLAlchemy ORM и изо всех сил пытаюсь выполнить сложные запросы к нескольким таблицам - запросы, которые я считаю относительно простыми в Doctrine DQL.
У меня есть объекты данных Города, принадлежащие Странам. В некоторых городах также установлен идентификатор округа, но не во всех. Помимо необходимых первичных и внешних ключей, каждая запись также имеет text_string_id, который ссылается на таблицу TextStrings, в которой хранится название города / округа / страны на разных языках. Таблица TextStrings MySQL выглядит следующим образом:
CREATE TABLE IF NOT EXISTS `text_strings` (
`id` INT UNSIGNED NOT NULL,
`language` VARCHAR(2) NOT NULL,
`text_string` varchar(255) NOT NULL,
PRIMARY KEY (`id`, `language`)
)
Я хочу построить для каждого города цепочку навигации в форме:
country_en_name> city_en_name OR
country_en_name> county_en_name> city_en_name,
в зависимости от того, установлен ли для этого города атрибут округа. В Doctrine это было бы относительно просто:
$query = Doctrine_Query::create()
->select('ci.id, CONCAT(cyts.text_string, \'> \', IF(cots.text_string is not null, CONCAT(cots.text_string, \'> \', \'\'), cits.text_string) as city_breadcrumb')
->from('City ci')
->leftJoin('ci.TextString cits')
->leftJoin('ci.Country cy')
->leftJoin('cy.TextString cyts')
->leftJoin('ci.County co')
->leftJoin('co.TextString cots')
->where('cits.language = ?', 'en')
->andWhere('cyts.language = ?', 'en')
->andWhere('(cots.language = ? OR cots.language is null)', 'en');
С SQLAlchemy ORM я изо всех сил пытаюсь добиться того же. Мне кажется, что я правильно настроил объекты - например, в форме:
class City(Base):
__tablename__ = "cities"
id = Column(Integer, primary_key=True)
country_id = Column(Integer, ForeignKey('countries.id'))
text_string_id = Column(Integer, ForeignKey('text_strings.id'))
county_id = Column(Integer, ForeignKey('counties.id'))
text_strings = relation(TextString, backref=backref('cards', order_by=id))
country = relation(Country, backref=backref('countries', order_by=id))
county = relation(County, backref=backref('counties', order_by=id))
Моя проблема заключается в запросах - я пробовал различные подходы к созданию хлебных крошек, но, похоже, ничего не работает. Некоторые наблюдения:
Возможно, использование таких вещей, как CONCAT и IF, встроенное в запрос, не очень питонично (возможно ли это даже с ORM?) - поэтому я попытался выполнить эти операции вне SQLAlchemy, в цикле записей Python . Однако здесь я изо всех сил пытался получить доступ к отдельным полям - например, аксессоры модели, похоже, не заходят на n уровней, например City.counties.text_strings.language не существует.
Я также экспериментировал с использованием кортежей - ближе всего к этому у меня получилось разбить его на два запроса:
# For cities without a county
for city, country in session.query(City, Country).\
filter(Country.id == City.country_id).\
filter(City.county_id == None).all():
if city.text_strings.language == 'en':
# etc
# For cities with a county
for city, county, country in session.query(City, County, Country).\
filter(and_(City.county_id == County.id, City.country_id == Country.id)).all():
if city.text_strings.language == 'en':
# etc
Я разделил его на два запроса, потому что я не мог понять, как сделать присоединение костюма необязательным в одном запросе. Но этот подход, конечно, ужасен, и, что еще хуже, второй запрос не работал на 100% - он не объединял все разные строки city.text_strings для последующей фильтрации.
Так я в тупике! Любая помощь, которую вы можете мне оказать, направив меня на правильный путь для выполнения такого рода сложных запросов в SQLAlchemy ORM, была бы очень признательна.