使用SQLAlchemy导入带外键的假数据?
我最近在尝试用fixture来加载测试数据集到我的Pylons / PostgreSQL应用中。这一切都很顺利,除了在创建外键时,如果外键指向的是一个自动递增的ID字段,就会出现问题。
我的fixture看起来是这样的:
class AuthorsData(DataSet):
class frank_herbert:
first_name = "Frank"
last_name = "Herbert"
class BooksData(DataSet):
class dune:
title = "Dune"
author_id = AuthorsData.frank_herbert.ref('id')
而模型则是:
t_authors = sa.Table("authors", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("first_name", sa.types.String(100)),
sa.Column("last_name", sa.types.String(100)),
)
t_books = sa.Table("books", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("title", sa.types.String(100)),
sa.Column("author_id", sa.types.Integer, sa.ForeignKey('authors.id'))
)
当我运行“paster setup-app development.ini”时,SQLAlchemy报告外键的值为“None”,显然它没有找到这个值:
15:24:27,284 INFO [sqlalchemy.engine.base.Engine.0x...9f50]
CREATE TABLE authors (
id SERIAL NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
PRIMARY KEY (id)
)
15:24:27,284 INFO [sqlalchemy.engine.base.Engine.0x...9f50] {}
15:24:27,291 INFO [sqlalchemy.engine.base.Engine.0x...9f50] COMMIT
15:24:27,292 INFO [sqlalchemy.engine.base.Engine.0x...9f50]
CREATE TABLE books (
id SERIAL NOT NULL,
title VARCHAR(100),
author_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(author_id) REFERENCES authors (id)
)
15:24:27,293 INFO [sqlalchemy.engine.base.Engine.0x...9f50] {}
15:24:27,300 INFO [sqlalchemy.engine.base.Engine.0x...9f50] COMMIT
15:24:27,301 INFO [blog.websetup] Inserting initial data
15:24:27,302 INFO [sqlalchemy.engine.base.Engine.0x...9f50] BEGIN
15:24:27,309 INFO [sqlalchemy.engine.base.Engine.0x...9f50] INSERT INTO authors (first_name, last_name) VALUES (%(first_name)s, %(last_name)s) RETURNING authors.id
15:24:27,309 INFO [sqlalchemy.engine.base.Engine.0x...9f50] {'first_name': 'Frank', 'last_name': 'Herbert'}
15:24:27,320 INFO [sqlalchemy.engine.base.Engine.0x...9f50] INSERT INTO books (title, author_id) VALUES (%(title)s, %(author_id)s) RETURNING books.id
15:24:27,320 INFO [sqlalchemy.engine.base.Engine.0x...9f50] {'author_id': None, 'title': 'Dune'}
15:24:27,322 INFO [sqlalchemy.engine.base.Engine.0x...9f50] COMMIT
15:24:27,323 INFO [blog.websetup] Done
fixture的文档实际上警告说这可能会是个问题:
“然而,在某些情况下,你可能需要引用一个在加载之前没有值的属性,比如一个序列ID列。(注意,当使用会话时,SQLAlchemy的数据层不支持这一点。)”
http://farmdev.com/projects/fixture/using-dataset.html#referencing-foreign-dataset-classes
这是否意味着SQLAlchemy根本不支持这个功能?还是说可以在不使用SA“会话”的情况下加载数据?其他人是怎么解决这个问题的呢?
3 个回答
我通过这样定义 orm.relation
来解决这个问题:
class BooksData(DataSet):
class dune:
title = "Dune"
author = AuthorsData.frank_herbert
mapper(Book, books, properties={
'author': relation(Author)
})
你不能直接写 author = AuthorsData.frank_herbert
来代替直接指定 author_id
吗?
除此之外,也许在插入你的 AuthorsData 之后调用 meta.Session.flush()
会强制让 id 属性获得值?我在测试时就是这么做的,当我需要分配 ID 时(我不使用固定数据)。
在SQLAlchemy中,SQL表达式层不支持引用还没有插入的行,因为这样没有意义:所有的查询都是明确执行的。ORM层(对象关系映射层)支持引用新对象,它甚至使用了一种叫做“工作单元模式”的方法来正确安排插入的顺序。为了解决这个问题,工具使用了ref()
方法。不过要注意,在SQL表达式层,你(工具)执行的是语句,所以SQLAlchemy没有机会重新排序这些语句。因此,如果你试图在插入作者之前存储书籍,author_id
就无法使用。你确定你的工具是按照正确的顺序组织(传递给data()
方法)的吗?