如何在SQLAlchemy ORM中查询多个表

Ale*_*ean 5 python doctrine sqlalchemy

我是SQLAlchemy ORM的新手,我正在努力在多个表上完成复杂的查询 - 我发现在Doctrine DQL中相对简单的查询.

我有城市的数据对象,属于国家.一些城市也有县ID,但不是全部.除了必要的主键和外键之外,每个记录还有一个text_string_id,它链接到TextStrings表,该表存储不同语言的City/County/Country的名称.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`)
)
Run Code Online (Sandbox Code Playgroud)

我想为每个城市构建一个面包屑,形式如下:

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');
Run Code Online (Sandbox Code Playgroud)

使用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))
Run Code Online (Sandbox Code Playgroud)

我的问题在于查询 - 我尝试了各种方法来生成面包屑,但似乎没有任何效果.一些观察:

也许在查询中使用CONCAT和IF内联等内容并不是非常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
Run Code Online (Sandbox Code Playgroud)

我将它分成两个查询,因为我无法弄清楚如何在一个查询中使Suit连接成为可选项.但是这种方法当然是可怕的,更糟糕​​的是第二个查询不能100%工作 - 它没有加入所有不同的city.text_strings进行后续过滤.

所以我很难过!您可以给我的任何帮助,让我在SQLAlchemy ORM中执行这些复杂问题的正确途径,我将不胜感激.

zzz*_*eek 5

映射Suit不存在,但基于推进查询,我认为它具有text_strings属性.

描述带连接别名的SQLAlchemy文档的相关部分位于:

http://www.sqlalchemy.org/docs/orm/tutorial.html#using-aliases

一代功能是:

http://www.sqlalchemy.org/docs/core/tutorial.html#functions

cyts = aliased(TextString)
cits = aliased(TextString)
cots = aliased(TextString)
cy = aliased(Suit)
co = aliased(Suit)

session.query(
            City.id, 
            (
                cyts.text_string + \
                '> ' + \
                func.if_(cots.text_string!=None, cots.text_string + '> ', cits.text_string)
            ).label('city_breadcrumb')
            ).\
            outerjoin((cits, City.text_strings)).\
            outerjoin((cy, City.country)).\
            outerjoin((cyts, cy.text_strings)).\
            outerjoin((co, City.county))\
            outerjoin((cots, co.text_string)).\
            filter(cits.langauge=='en').\
            filter(cyts.langauge=='en').\
            filter(or_(cots.langauge=='en', cots.language==None))
Run Code Online (Sandbox Code Playgroud)

虽然我会认为只是简单地说:

city.text_strings.text_string + " > " + city.country.text_strings.text_string + " > " city.county.text_strings.text_string
Run Code Online (Sandbox Code Playgroud)

如果你在City上放置描述符,套装:

class City(object):
   # ...
   @property
   def text_string(self):
      return self.text_strings.text_string
Run Code Online (Sandbox Code Playgroud)

然后你可以说city.text_string.