SQLAlchemy:按关系中的关系字段排序

Rol*_*ish 5 python sqlalchemy pyramid

在我正在处理的Pyramid应用程序中,我遇到以下情况:

class Widget(Base):
    __tablename__ = 'widgets'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    sidebar = Column(mysql.TINYINT(2))

    def __init__(self, name, sidebar):
        self.name = name
        self.sidebar = sidebar

class Dashboard(Base):
    __tablename__ = 'dashboard'
    user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
    widget_id = Column(Integer, ForeignKey('widgets.id'), primary_key=True)
    delta = Column(mysql.TINYINT)

    widget = relationship('Widget')

    def __init__(self, user_id, widget_id, delta):
        self.user_id = user_id
        self.widget_id = widget_id
        self.delta = delta 

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    login = Column(Unicode(255), unique=True)
    password = Column(Unicode(60))
    fullname = Column(Unicode(100))

    dashboard = relationship('Dashboard', order_by='Dashboard.widget.sidebar, Dashboard.delta')

    def __init__(self, login, password, fullname):
        self.login = login
        self.password = crypt.encode(password)
        self.fullname = fullname
Run Code Online (Sandbox Code Playgroud)

因此,我希望用户“仪表板”关系具有该用户的仪表板记录,但按“侧栏”排序(这是仪表板的关系属性)。目前,我收到此错误:

sqlalchemy.exc.InvalidRequestError: Property 'widget' is not an instance of ColumnProperty (i.e. does not correspond directly to a Column).
Run Code Online (Sandbox Code Playgroud)

关系声明中是否可以进行这种排序?

谢谢!

zzz*_*eek 5

有了这个,试着思考 SQLAlchemy 在尝试加载 User.dashboard 时应该发出什么。喜欢SELECT * FROM dashboard JOIN widget ... ORDER BY widget.sidebar ?或者SELECT * FROM dashboard ORDER BY (SELECT sidebar FROM widget...?按不同的表对结果进行排序对于一项工作来说太开放了,relationship()无法自行决定。可以做到这一点的方法是通过提供一个列表达式Dashboard来提供这种排序,当 ORM 针对仪表板的表发出简单的 SELECT 时,以及当它在不那么简单的 SELECT 中引用它时可能会同时加入用户、仪表板表(例如预先加载)。

我们提供自定义 SQL 表达式,特别是那些涉及其他表的表达式,使用column_property(),或者当我们不希望默认加载该表达式时使用deferred()(就像这里的情况一样)。例子:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()

class Widget(Base):
    __tablename__ = 'widgets'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    sidebar = Column(Integer)

class Dashboard(Base):
    __tablename__ = 'dashboard'
    user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
    widget_id = Column(Integer, ForeignKey('widgets.id'), primary_key=True)
    delta = Column(Integer)

    widget = relationship('Widget')

    widget_sidebar = deferred(select([Widget.sidebar]).where(Widget.id == widget_id))

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    login = Column(Unicode(255), unique=True)

    dashboard = relationship('Dashboard', order_by='Dashboard.widget_sidebar, Dashboard.delta')


e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

w1, w2 = Widget(name='w1', sidebar=1), Widget(name='w2', sidebar=2)
s.add_all([
    User(login='u1', dashboard=[
        Dashboard(
            delta=1, widget=w1
        ),
        Dashboard(
            delta=2, widget=w2
        )
    ]),
])
s.commit()

print s.query(User).first().dashboard
Run Code Online (Sandbox Code Playgroud)

“.dashboard”的负载发出的最终 SQL 是:

SELECT dashboard.user_id AS dashboard_user_id, dashboard.widget_id AS dashboard_widget_id, dashboard.delta AS dashboard_delta 
FROM dashboard 
WHERE ? = dashboard.user_id ORDER BY (SELECT widgets.sidebar 
FROM widgets 
WHERE widgets.id = dashboard.widget_id), dashboard.delta
Run Code Online (Sandbox Code Playgroud)

请记住,MySQL 在优化上面的子查询方面做得很糟糕。如果您在这里需要高性能,您可以考虑将“sidebar”的值复制到“dashboard”中,即使这样会使一致性更难以维护。