【问题标题】:SQLAlchemy: order by a relationship field in a relationshipSQLAlchemy:按关系中的关系字段排序
【发布时间】:2013-11-03 08:49:53
【问题描述】:

在我正在处理的 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

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

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

这种排序可以在关系声明中使用吗?

谢谢!

【问题讨论】:

    标签: python sqlalchemy pyramid


    【解决方案1】:

    有了这个,试着想想 SQL SQLAlchemy 在尝试加载 User.dashboard 时应该发出什么。喜欢SELECT * FROM dashboard JOIN widget ... ORDER BY widget.sidebar 吗?还是SELECT * FROM dashboard ORDER BY (SELECT sidebar FROM widget...?按不同的表排序结果对于relationship() 来说太开放了,无法自行决定。可以做到这一点的方法是,当 ORM 针对仪表板的表发出一个简单的 SELECT 时,以及当它以不那么简单的方式引用它时,提供一个可以提供此排序的 Dashboard 的列表达式选择它可能会同时跨用户、仪表板表加入的位置(例如急切加载)。

    我们提供自定义 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
    

    加载“.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
    

    请记住,MySQL 对上述子查询的优化是一项糟糕的工作。如果您在此处需要高性能,则可以考虑将“sidebar”的值复制到“dashboard”中,即使这会使一致性更难以维护。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      • 2012-08-27
      • 2019-04-12
      • 2021-08-28
      • 2021-02-07
      • 2020-01-12
      相关资源
      最近更新 更多