使用关联表时,查询会导致提交

时间:2012-02-06 22:45:23

标签: mysql sqlalchemy

有谁能帮我理解以下行为?我有一个简单的架构,其中一个项目有许多资产和元素。资产和要素在项目范围内具有多对多关系。

我需要确保一个Element有一个在Project范围内唯一的代码,所以在我向SQLAlchemy会话添加一个新元素之前,我想检查一个具有相同代码的Element是否已经存在在项目中。我发现,当我使用关联表来映射元素和资产之间的多对多关系时,如果没有查询操作实际将新元素提交给数据库,我就无法查询数据库。

要明确的是,此时我还没有添加'ed或提交'这个新元素到SQLAlchemy会话。

from sqlalchemy import create_engine, and_
from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relation, backref
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy

### your credentials here ###
# _MYSQL_DB = 
# _MYSQL_USER = 
# _MYSQL_PWORD = 
# _MYSQL_SCHEMATA = 
# _MYSQL_TEST_SCHEMATA = 

testEngine = create_engine("mysql://%s:%s@%s/%s"%(  _MYSQL_USER,
                                                    _MYSQL_PWORD,
                                                    _MYSQL_DB,
                                                    _MYSQL_TEST_SCHEMATA),
                                                    pool_recycle=3600,
                                                    echo=True)

Base = declarative_base()
SQLAlchemySession = sessionmaker(autoflush=True, autocommit=False)

class Project(Base):
    __tablename__ = "SA_project_t"
    id = Column(Integer, primary_key=True, autoincrement=True)
    code = Column(String(24), nullable=False, unique=True)

class Asset(Base):
    __tablename__ = "SA_asset_t"
    id = Column(Integer, primary_key=True, autoincrement=True)
    projectId = Column(Integer, ForeignKey("SA_project_t.id"))
    project = relation("Project")
    code = Column(String(128))

element_to_asset_assoc = Table( "SA_elementToAssetAssoc_t",
                                Base.metadata,
                                Column("elementId", Integer, ForeignKey("SA_element_t.id")),
                                Column("assetId", Integer, ForeignKey("SA_asset_t.id")))

class Element(Base):
    __tablename__ = "SA_element_t"
    id = Column(Integer, primary_key=True, autoincrement=True)
    projectId = Column(Integer, ForeignKey("SA_project_t.id"))
    project = relation("Project")
    code = Column(String(256))
    assets = relation("Asset", secondary=element_to_asset_assoc, backref="elements")


Base.metadata.bind=testEngine
session = SQLAlchemySession(bind=testEngine)

Base.metadata.drop_all(checkfirst=True)
Base.metadata.create_all(testEngine, checkfirst=True)

# Create a Project, Asset and Element

project = Project()
project.code = "MyProject"
session.add(project)

asset = Asset()
asset.project = project
asset.code = "MyAsset"
session.add(asset)

element = Element()
element.project = project
element.code = "MyElement"
element.assets = [asset]
session.add(element)

session.commit()

# Now I'd like to add a new element, but first check that the 
# element's code is unique within the scope of the Project

newElement = Element()
newElement.project = project
newElement.code = "MyElement"
newElement.assets = [asset]

results = session.query(Element).filter(and_( Element.project==newElement.project,
                                            Element.code==newElement.code))

# Up until this point, newElement hasn't been inserted into the 
# database, but once I query "results.count()" I find that an INSERT
# has been perfomed.  

print results.count()

# p.s. I realize that results will contain both the original and 
# new element, but I don't expect the new element to have been
# inserted into the database at this point.

结果变量上调用 count()的操作是将 newElement 提交到MySQL数据库,这不是我的意图。如果我删除关联表,那么我会得到我期望的行为 - newElement 不会添加到数据库中。

从日志中我可以看到,会话在查询开始时将关联的资产视为脏,并且刷新它会触发 newElement 的提交。这是预期的行为吗?如果是这样,有没有办法保持多对多关系,同时仍然在这些情况下查询数据库,触发提交。

使用SQLAlchemy 0.7.4

1 个答案:

答案 0 :(得分:1)

不需要add() newElement对象,因为当您将绑定对象分配给其中一个关系时,它会自动添加到会话中。访问newElement.project属性时,它会刷新到数据库(您可以使用autoflush=False禁用此行为)。 MySQL默认使用MyISAM引擎,忽略事务语句。因此,所有刷新的变化都会持续存在。要启用事务支持,请将__table_args__ = {'mysql_engine': 'InnoDB'}添加到映射的类中。