我们正在从Grails迁移到Python(和Flask)。我希望能够使用SQLAlchemy中Grails应用程序的当前数据库结构。
在Grails中,我有一些像这样的域类:
class Asset {
static hasMany = [documents:AssetDocument]
List<AssetDocument> documents = new ArrayList<AssetDocument>();
...
}
class AssetDocument {
...
}
这将在Postgresql中创建一些像这样的表
CREATE TABLE asset
(
id bigint NOT NULL,
version bigint NOT NULL,
... etc
)
CREATE TABLE asset_document
(
id bigint NOT NULL,
version bigint NOT NULL,
.... etc
)
最后
CREATE TABLE asset_asset_document
(
asset_documents_id bigint,
asset_document_id bigint,
documents_idx integer,
CONSTRAINT fk_4pqxhfusa9cevslboxbucsjdn FOREIGN KEY (asset_document_id)
REFERENCES asset_document (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
asset_asset_document表将Asset表连接到Asset_ 文件表。
在Grails中,因为我将关系声明为List,所以有一个Extra字段document_idx这是数组中的索引,所以对于像这样的表
资产
+-----+---------+-------+
| ID | Version | etc.. |
+-----+---------+-------+
| 1 | 0 | ..... |
| 2 | 0 | ..... |
+-----+---------+-------+
asset_document
+-----+---------+-------+
| ID | Version | etc.. |
+-----+---------+-------+
| 1 | 0 | ..... |
| 2 | 0 | ..... |
| 3 | 0 | ..... |
+-----+---------+-------+
asset_asset_document
+---------------------+--------------------+----------------+
| asset_documents_id | asset_document_id | documents_idx |
+---------------------+--------------------+----------------+
| 1 | 1 | 0 |
| 1 | 2 | 1 |
| 2 | 3 | 0 |
+---------------------+--------------------+----------------+
使用此示例资产,其中id = 1有两个asset_documents,其中ids = 1,2 where asset id = 2,有一个asset_document,id = 3
documents_idx字段根据AssetDocuments数组中的索引递增。
如何在SQLAlchemy中映射它,以便如果我将AssetDocument添加到我的资产关系中,document_idx也会更新?
例如,如果我将AssetDocument添加到资产中,其中id = 2
+---------------------+--------------------+----------------+
| asset_documents_id | asset_document_id | documents_idx |
+---------------------+--------------------+----------------+
| 1 | 1 | 0 |
| 1 | 2 | 1 |
| 2 | 3 | 0 |
| 2 | 5 | 1 |
+---------------------+--------------------+----------------+
到目前为止,我可以看到我需要
t_asset_asset_document = Table(
'asset_asset_document', metadata,
Column('asset_documents_id', BigInteger),
Column('asset_document_id', ForeignKey(u'asset_document.id')),
Column('documents_idx', Integer)
)
class Asset(Base):
__tablename__ = 'asset'
id = Column(BigInteger, Sequence('seq_asset'), primary_key=True)
version = Column(BigInteger, nullable=False)
documents = relationship("AssetDocument",
secondary=t_asset_asset_document) #What needs to be here??
class AssetDocument(Base):
__tablename__ = 'asset_document'
id = Column(BigInteger, Sequence('seq_asset_document'), primary_key=True)
version = Column(BigInteger, nullable=False)
答案 0 :(得分:4)
有Ordering List自动处理此类索引更新。但是,它并不适用于多对多关系。为了解决这个问题,可以使用Association Proxy扩展名。
基于您的模型的完整工作代码如下:
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.orderinglist import ordering_list
# ...
# :> convert relationship table to a mapped object
class AssetAssetDocument(Base):
__tablename__ = 'asset_asset_document'
asset_id = Column(ForeignKey(u'asset.id'), primary_key=True)
asset_document_id = Column(ForeignKey(u'asset_document.id'), primary_key=True)
documents_idx = Column(Integer)
# :> define one-way relationship; will be used by association proxy
document = relationship('AssetDocument')
class Asset(Base):
__tablename__ = 'asset'
id = Column(BigInteger, Sequence('seq_asset'), primary_key=True)
version = Column(BigInteger, nullable=False)
# :> define a relationship to a mapped relationship object
# this one is able to use `ordering_list` factory
_documents = relationship(
AssetAssetDocument,
order_by='AssetAssetDocument.documents_idx',
collection_class=ordering_list('documents_idx'),
)
# :> define a desired relationship
documents = association_proxy(
'_documents', 'document',
creator=lambda _d: AssetAssetDocument(document=_d),
)
class AssetDocument(Base):
__tablename__ = 'asset_document'
id = Column(BigInteger, Sequence('seq_asset_document'), primary_key=True)
version = Column(BigInteger, nullable=False)
用法很简单:
# INSERT TEST DATA
a1 = Asset(
version=0,
documents=[
AssetDocument(version=1),
AssetDocument(version=2),
])
session.add(a1)
session.commit()
# TEST MODIFICATIONS
# remove first one
del a1.documents[0]
# insert a position 0
a1.documents.insert(0, AssetDocument(version=3))
# add at the end
a1.documents.append(AssetDocument(version=4))
session.commit()
# check that the document_idx are properly ordered
q = (session
.query(AssetAssetDocument.asset_document_id)
.filter(AssetAssetDocument.asset_id == a1.id)
.order_by(AssetAssetDocument.documents_idx)
)
doc_ids = [x.asset_document_id for x in q]
assert doc_ids == [3, 2, 4]