【问题标题】:delete not cascaded to table in sqlalchemy删除未级联到 sqlalchemy 中的表
【发布时间】:2012-01-29 19:49:57
【问题描述】:

我正在开发一个使用 sqlalchemy 0.6 的现有应用程序的扩展。

该应用程序具有以非声明方式创建的 sqlalchemy 表。我正在尝试在我的扩展中创建一个新表,其中的外键列指向应用程序数据库中主表的主键,并且我正在以声明方式创建它。

这一切都很好,一旦加载扩展程序就创建了表格,而且没有任何抱怨。我的表格打印出来并证明新行已添加好。 我想要并且认为是可能的(但不知道,因为我从未使用过 sql 或任何其他数据库)是在删除应用程序主表中具有相应外键的行时删除表中的相应行.

到目前为止,已经尝试了许多排列,但没有任何效果。我认为使用 backref 集和用 delete 定义的关系被级联,应该没有问题。因为新表是在应该只是插件的扩展中定义的,所以我根本不想在主应用程序中编辑代码,至少这是我的目标。但是,我遇到的问题之一是我要引用的主应用程序表在其类中未定义成员变量,未在其映射器中声明其主键,并且仅在表中声明了主键.这使得很难创建一个关系(船)子句,其第一个参数必须是一个类或映射器(在这种情况下,它们都没有声明主键)。 有什么方法可以实现吗?

ps - 这是我正在使用的一些代码。 LocalFile 是声明性类。所有连接细节都由主应用程序处理。

    if not self.LocalFile.__table__.exists(bind=Engine):
        self.LocalFile__table__.create(bind=Engine)

这是 LocalFile 类 - Base 是一个声明性基类,在构造函数中传递了 bind=Engine:

   class LocalFile(Base):
    __tablename__ = 'local_file'
    _id = Column(Integer, Sequence('local_file_sequence', start=1, increment=1), primary_key=True)
    _filename = Column(String(50), nullable=False)
    _filepath = Column(String(128), nullable=False)
    _movieid = Column(Integer, ForeignKey(db.tables.movies.c.movie_id, onupdate='CASCADE', ondelete='CASCADE'))
    #movies = relation(db.Movie, backref="local_file", cascade="all")

    @property
    def filename(self):
        return self._filename

    @filename.setter
    def filename(self, filename):
        self._filename = filename

    @property
    def filepath(self):
        return self._filepath

    @filepath.setter
    def filepath(self, filepath):
        self._filepath = filepath

    @property
    def movieid(self):
        return self._movieid

    @movieid.setter
    def movieid(self, movieid):
        self._movieid = movieid

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, id):
        self._id = id

    filename = synonym('_filename', descriptor=filename)
    movieid = synonym('_movieid', descriptor=movieid)
    filepath = synonym('_filepath', descriptor=filepath)
    id = synonym('_id', descriptor=id)

    def __init__(self, filename, filepath, movieid):
        self._filename = filename
        self._filepath = filepath
        self._movieid = movieid

    def __repr__(self):
        return "<User('%s','%s', '%s')>" % (self.filename, self.filepath, self.movieid)

编辑:

后端是 sqlite3。下面是使用 echo 命令创建表的代码(感谢您指出这一点,它非常有用 - 我已经怀疑现有应用程序生成的 sql 远远超出了必要的数量)。 跟在报告的sql表创建之后是删除一行时生成的代码。我个人看不到任何引用本地文件表中可能删除一行的语句,但我目前对sql知之甚少。谢谢。

     2011-12-29 16:29:18,530 INFO sqlalchemy.engine.base.Engine.0x...0650 
     CREATE TABLE local_file (
_id INTEGER NOT NULL, 
_filename VARCHAR(50) NOT NULL, 
_filepath VARCHAR(128) NOT NULL, 
_movieid INTEGER, 
PRIMARY KEY (_id), 
FOREIGN KEY(_movieid) REFERENCES movies (movie_id) ON DELETE CASCADE ON UPDATE CASCADE

)

    2011-12-29T16:29:18: I: sqlalchemy.engine.base.Engine.0x...0650(base:1387): 
    CREATE TABLE local_file (
_id INTEGER NOT NULL, 
_filename VARCHAR(50) NOT NULL, 
_filepath VARCHAR(128) NOT NULL, 
_movieid INTEGER, 
PRIMARY KEY (_id), 
FOREIGN KEY(_movieid) REFERENCES movies (movie_id) ON DELETE CASCADE ON UPDATE CASCADE

)

2011-12-29 16:29:18,534 INFO sqlalchemy.engine.base.Engine.0x...0650 ()
2011-12-29T16:29:18: I: sqlalchemy.engine.base.Engine.0x...0650(base:1388): ()
2011-12-29 16:29:18,643 INFO sqlalchemy.engine.base.Engine.0x...0650 COMMIT
2011-12-29T16:29:18: I: sqlalchemy.engine.base.Engine.0x...0650(base:1095): COMMIT

for row in table 为两个表生成以下内容:

本地文件表: (, u' 310 To Yuma') (,你'贪婪')

现有应用中的电影表: (, u'IMDb - 3:10 to Yuma') (,你'贪婪')

删除一行时的代码太长了,我无法在此处包含它(200 行左右 - 删除一行是不是有点太多了?),但它没有提及删除行中的一行本地文件表。有这样的语句:

   2011-12-29 17:09:17,141 INFO sqlalchemy.engine.base.Engine.0x...0650 UPDATE movies SET   poster_md5=?, updated=? WHERE movies.movie_id = ?
   2011-12-29T17:09:17: I: sqlalchemy.engine.base.Engine.0x...0650(base:1387): UPDATE movies SET poster_md5=?, updated=? WHERE movies.movie_id = ?
   2011-12-29 17:09:17,142 INFO sqlalchemy.engine.base.Engine.0x...0650 (None, '2011-12-29 17:09:17.141019', 2)
   2011-12-29T17:09:17: I: sqlalchemy.engine.base.Engine.0x...0650(base:1388): (None, '2011-12-29 17:09:17.141019', 2)
   2011-12-29 17:09:17,150 INFO sqlalchemy.engine.base.Engine.0x...0650 DELETE FROM posters WHERE posters.md5sum = ?
   2011-12-29T17:09:17: I: sqlalchemy.engine.base.Engine.0x...0650(base:1387): DELETE FROM posters WHERE posters.md5sum = ?
   2011-12-29 17:09:17,157 INFO sqlalchemy.engine.base.Engine.0x...0650 (u'083841e14b8bb9ea166ea4b2b976f03d',)

【问题讨论】:

  • 您能否请转echo=True,确保表local_file 不存在,然后运行您的代码并检查/发布为创建表而生成的SQL。另请提及您正在使用的数据库后端。
  • 嘿,范,刚刚完成 - 编辑了主要帖子以包含代码。后端是 sqlite3。谢谢!
  • 仅供参考:如果您注意到,您会看到每个语句记录了两次。当您同时拥有 echo=True 并明确配置 sqlalchemy 包的日志记录时,就会发生这种情况......

标签: python sql orm foreign-keys sqlalchemy


【解决方案1】:

在 SQLite 中,您必须显式打开对外键的支持,否则它会忽略任何与外键相关的 SQL。

engine = create_engine(database_url)

def on_connect(conn, record):
    conn.execute('pragma foreign_keys=ON')

from sqlalchemy import event
event.listen(engine, 'connect', on_connect)

【讨论】:

  • 非常感谢。那行得通,我可能永远不会自己找到答案。您是否知道是否有一种方法可以添加延迟的 PRAGMA,以便我可以在插件初始化时添加编译指示(这将在执行连接语句之后)。谢谢
  • 您可以在初始化插件时自己手动执行,但这听起来更适用于应用程序。
  • 再次感谢,到目前为止,在确定如何将多个 pragma 定义传递给单个执行语句时遇到问题。 (编译指示定义之间是否有分号?-我的数据库尚未连接多个传递给执行语句的编译指示。语法示例和解释似乎不存在。我不得不说,对于我认为是开发中如此重要的一部分,sql的教程和信息似乎有点差。我明天会专门问这个问题。
  • 啊哈 - 现在我明白了 - 我可以随时使用任何类型的 pragma 语句调用 connection.execute ,只要使用该选项编译 sqlite 我就笑了。呵呵。全做完了。 :-)
猜你喜欢
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 2020-01-03
  • 2011-10-12
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多