Я новичок в ORM SQLAlchemy, и я изо всех сил пытаюсь выполнить сложные запросы на нескольких таблицах - запросы, которые я считаю относительно простыми в Doctrine DQL.Как запросить несколько таблиц в SQLAlchemy ORM
У меня есть объекты данных городов, которые принадлежат странам. Некоторые города также имеют идентификационный номер округа, но не все. Как и необходимые первичные и внешние ключи, каждая запись также имеет 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 ИЛИ
country_en_name> county_en_name> city_en_name,
в зависимости от того, установлен ли атрибут округа для этого города. В доктрине это было бы относительно просто:
$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 ОРМ, я изо всех сил, чтобы достичь того же. Я считаю, что я установки объектов правильно - в форме, например:
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 inline в запросе, не очень pythonic (возможно ли это с 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
Я разделил его на два запроса, потому что я не мог выясните, как сделать членство Suit необязательным только в одном запросе. Но этот подход, конечно, страшный и худший, второй запрос не работал на 100% - он не соединял все разные city.text_strings для последующей фильтрации.
Так что я в тупике! Любая помощь, которую вы можете дать мне, чтобы установить меня на правильном пути для выполнения подобных видов сложных запросов в SQLAlchemy ORM, будет очень признательна.
Огромное спасибо Майку за этот ответ - я должен был использовать псевдонимы! Как только я обновил свою SQLAlchemy до более новой версии, ваш код работал нормально. В конце концов я немного адаптировал ваш код - я вставлю свой код ниже в качестве отдельного ответа, если кто-то захочет его увидеть. Один последний момент: вы говорите: «Я бы подумал, что его чертовски просто проще сказать: city.text_strings.text_string ...» Я пробовал делать что-то подобное, но этот синтаксис, похоже, не уважал внешности, т.е. свойства text_string для языка == 'de' вместо языка == 'en'. Я не уверен, что я делаю что-то неправильно! –