如何使用sortedcontainers.SortedList与SQLAlchemy关系()?

时间:2017-02-20 14:08:33

标签: python sqlalchemy

我有一个多对一关系的数据库。在我的声明系统中,我调用relationship(),如下面的最小示例所示。出于某种原因,我想保持所有Features排序。不幸的是,排序逻辑非常复杂,使用了几个属性。

from sortedcontainers import SortedList
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, Text
from sqlalchemy import ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(Text)

class Feature(Base):
    __tablename__ = 'features'
    id = Column(Integer, primary_key=True, autoincrement=True)
    productid = Column(Integer, ForeignKey('products.id'))
    attr = Column(Integer)

    product = relationship('Product', back_populates='features')
    Product.features=relationship('Feature', collection_class=SortedList, back_populates='product')

    def __eq__(self, other):
        # some magic here, simplified...
        return self.attr == other.attr

    def __lt__(self, other):
        # some magic here, simplified...
        return self.attr < other.attr

    def __le__(self, other):
        # some magic here, simplified...
        return self.attr <= other.attr

engine = create_engine('sqlite:///test.db')
Session = sessionmaker(bind=engine)
Base.metadata.create_all(engine)

我的数据库已经填充了以下值:

sqlite> select * from products;
id          name      
----------  ----------
1           Product1  
sqlite> select * from features;
id          productid   attr      
----------  ----------  ----------
1           1           42        
2           1           1      

当我尝试加载(通过访问)这些值时,sortedcontainers.SortedList引发了一个ValueError异常,可能是因为它试图加载无序结果。当功能存储有序时,没有问题。

In [1]: from test import *
In [2]: session = Session()
In [3]: p = session.query(Product).first()
In [4]: for f in p.features:
    print("id: %d\tattr: %d" % (f.id, f.attr))
   ...:     
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
# traceback
ValueError: <test.Feature object at 0x7f68da5d2208> not in sort order at index 1

我知道在这种特殊情况下,解决方案是使用带有参数relationship的{​​{1}}()但我的用例更复杂,因为排序使用了几列的值。

那么,有没有我错过使用SQLAlchemy或我应该尝试找到一个解决方法?

1 个答案:

答案 0 :(得分:0)

是的,我终于找到了一个非常简单的解决方案来查看回溯:

/usr/lib/python3/dist-packages/sqlalchemy/orm/collections.py in append(self, item, _sa_initiator)
   1072         def append(self, item, _sa_initiator=None):
   1073             item = __set(self, item, _sa_initiator)
-> 1074             fn(self, item)
   1075         _tidy(append)
   1076         return append

/usr/lib/python3/dist-packages/sortedcontainers/sortedlist.py in append(self, val)
   1026         if val < _lists[pos][-1]:
   1027             msg = '{0} not in sort order at index {1}'.format(repr(val), self._len)
-> 1028             raise ValueError(msg)
   1029 
   1030         _maxes[pos] = val

显然,我们在append上呼叫SortedList。如果值违反排序顺序,则从documentation append引发ValueError。

基本上解决方案是覆盖该方法:

class SuperSortedList(SortedList):
    def append(self, item):
        self.add(item)

这似乎甚至在我的现实生活中也起作用。不过,覆盖所有类型的方法(例如insert)可能是个好主意。