如何使用 Factory Boy 和 Flask-SQLAlchemy 设置依赖工厂?
How do I setup dependent factories using Factory Boy and Flask-SQLAlchemy?
在 Flask-SQLAlchemy 和外键约束下使用 factory boy 的正确方法是什么?
考虑以下 Flask SQLAlchemy 模型设置:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.create_all()
用户模式在创建新用户时需要组外键。由于组主键是从数据库分配的,因此工厂需要提交组条目和获取条目的主键,以便它可以将其提供给新用户。
如何创建组,将其保存到数据库,并将其密钥提供给用户工厂?
Factory Boy has examples for dealing with Foreign Keys 但它们似乎不适用于 SQLAlchemy。以下是工厂和故障点:
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, RelatedFactory
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
# group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = RelatedFactory(GroupFactory)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.group_pk is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None
问题来自于使用 RelatedFactory
:这些用于 reverse ForeignKey
关系(例如,如果你想建立一个 Group
对象已经包含 User
).
对于直接 ForeignKey
- 比如从 User
到 Group
的关系,使用 SubFactory
:
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = db.session
# No need to force the user_pk, it is built automatically from the database
# user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = factory.SubFactory(GroupFactory)
我对 Flask-SQLAlchemy 不是很熟悉,但我刚刚向存储库(位于 https://github.com/rbarrois/factory_boy/tree/master/examples/flask_alchemy)添加了一个小示例,它与您的情况非常相似。
您可以使用 LazyAttribute 和 lambda 生成新组,然后将其 'group_pk' 拉出。
以下代码的工作版本:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.drop_all()
db.create_all()
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, LazyAttribute
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = LazyAttribute(lambda a: GroupFactory().group_pk)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None
Xelnor 的 git link 显示了迄今为止的最佳答案,但它需要更改 sqlalchemy 模型。这是我的 post 使用 Xelnor 的解决方案完成的工作副本:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
SQL炼金术Table模型
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'user'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=False)
'group' db.relationship 是使 SubFactory 调用起作用的原因。
UserFactory 将 group 传递给 User 模型,这是用这个设置的
关系()定义。
group = db.relationship('Group', backref=db.backref('groups', lazy='dynamic'))
def __init__(self, name, group):
self.name = name
self.group = group
def __repr__(self):
return '<Group for %r: %s>' % (self.group, self.name)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'group'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Group %r>' % self.name
创建 SQL 个表
db.create_all()
Factory-Boy 用户和组工厂
from factory import alchemy, Sequence, SubFactory, fuzzy
class BaseFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
abstract = True
sqlalchemy_session = db.session
class GroupFactory(BaseFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = fuzzy.FuzzyText()
class UserFactory(BaseFactory):
class Meta:
model = User
sqlalchemy_session = db.session
name = fuzzy.FuzzyText()
group = SubFactory(GroupFactory)
出厂测试
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.id is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.id == group_from_factory.id).first()
assert group_from_db.id is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.id == group_from_factory.id
# Create a new User from our factory
user1_from_factory = UserFactory(name=u'first')
user2_from_factory = UserFactory(name=u'second')
db.session.add(user1_from_factory)
db.session.add(user2_from_factory)
db.session.commit()
assert user1_from_factory.id is not None
assert user1_from_factory.name is not None
assert user1_from_factory.id is not None
assert user2_from_factory.id is not None
assert user2_from_factory.name is not None
assert user2_from_factory.id is not None
在 Flask-SQLAlchemy 和外键约束下使用 factory boy 的正确方法是什么?
考虑以下 Flask SQLAlchemy 模型设置:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.create_all()
用户模式在创建新用户时需要组外键。由于组主键是从数据库分配的,因此工厂需要提交组条目和获取条目的主键,以便它可以将其提供给新用户。
如何创建组,将其保存到数据库,并将其密钥提供给用户工厂?
Factory Boy has examples for dealing with Foreign Keys 但它们似乎不适用于 SQLAlchemy。以下是工厂和故障点:
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, RelatedFactory
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
# group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = RelatedFactory(GroupFactory)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.group_pk is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None
问题来自于使用 RelatedFactory
:这些用于 reverse ForeignKey
关系(例如,如果你想建立一个 Group
对象已经包含 User
).
对于直接 ForeignKey
- 比如从 User
到 Group
的关系,使用 SubFactory
:
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = db.session
# No need to force the user_pk, it is built automatically from the database
# user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = factory.SubFactory(GroupFactory)
我对 Flask-SQLAlchemy 不是很熟悉,但我刚刚向存储库(位于 https://github.com/rbarrois/factory_boy/tree/master/examples/flask_alchemy)添加了一个小示例,它与您的情况非常相似。
您可以使用 LazyAttribute 和 lambda 生成新组,然后将其 'group_pk' 拉出。
以下代码的工作版本:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.drop_all()
db.create_all()
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, LazyAttribute
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = LazyAttribute(lambda a: GroupFactory().group_pk)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None
Xelnor 的 git link 显示了迄今为止的最佳答案,但它需要更改 sqlalchemy 模型。这是我的 post 使用 Xelnor 的解决方案完成的工作副本:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
SQL炼金术Table模型
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'user'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=False)
'group' db.relationship 是使 SubFactory 调用起作用的原因。 UserFactory 将 group 传递给 User 模型,这是用这个设置的 关系()定义。
group = db.relationship('Group', backref=db.backref('groups', lazy='dynamic'))
def __init__(self, name, group):
self.name = name
self.group = group
def __repr__(self):
return '<Group for %r: %s>' % (self.group, self.name)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'group'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Group %r>' % self.name
创建 SQL 个表
db.create_all()
Factory-Boy 用户和组工厂
from factory import alchemy, Sequence, SubFactory, fuzzy
class BaseFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
abstract = True
sqlalchemy_session = db.session
class GroupFactory(BaseFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = fuzzy.FuzzyText()
class UserFactory(BaseFactory):
class Meta:
model = User
sqlalchemy_session = db.session
name = fuzzy.FuzzyText()
group = SubFactory(GroupFactory)
出厂测试
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.id is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.id == group_from_factory.id).first()
assert group_from_db.id is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.id == group_from_factory.id
# Create a new User from our factory
user1_from_factory = UserFactory(name=u'first')
user2_from_factory = UserFactory(name=u'second')
db.session.add(user1_from_factory)
db.session.add(user2_from_factory)
db.session.commit()
assert user1_from_factory.id is not None
assert user1_from_factory.name is not None
assert user1_from_factory.id is not None
assert user2_from_factory.id is not None
assert user2_from_factory.name is not None
assert user2_from_factory.id is not None