【问题标题】:How do I make the fixtures library ignore foreign keys in SQLalchemy如何使夹具库忽略 SQLalchemy 中的外键
【发布时间】:2015-02-05 08:21:17
【问题描述】:

我正在为使用旧版 MSSQL 数据库的 Web 应用程序开发一些测试。该数据库相当大,并且有许多与外键互连的表。我正在使用fixture library 创建包含测试数据的表,基于我在SQLalchemy 的应用程序中定义的模型。

我遇到的问题是,当我将一个表添加到具有外键的设备中时,运行测试还需要我添加连接的表,即使我没有使用它。

ProgrammingError: (ProgrammingError) ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Foreign key 'FK__countries__ctry___6C40A441' references invalid table 'directdebit_types'. (1767) (SQLExecDirectW); [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Could not create constraint.

例如,我需要“俱乐部”数据,但俱乐部是“区域”的一部分;我需要在我的灯具中添加一个“区域”表才能继续。添加“区域”后,由于区域链接到国家/地区,因此无法创建表格,所以现在我需要将国家/地区表格添加到我的灯具中。国家再次与 directdebit_types 相关联,所以现在我也需要添加它,即使我只想模拟一些俱乐部数据!

上面的示例错误是我每次开始测试时都会遇到的错误,并且fixture 会尝试设置表。在这种特定情况下,它是从国家到 direcdebit_types 的外键。 跟踪和创建所有互连的表将导致实际上必须创建整个数据库以进行简单测试。

有没有办法解决这个问题?

为了完整起见,这是我的夹具设置的相关部分:

class ClubsData(DataSet):
    """
    Fixture dataset class for Clubs
    """
    class accesscontrol_test_club:
        club_id = '22222222-2222-2222-2222-222222222222'
        club_name = 'accesscontrol test country'


class AreasData(DataSet):
    """
    Fixture dataset class for Areas
    """
    class accesscontrol_test_area:
        area_name = 'testarea'
        club = ClubsData.accesscontrol_test_club


class CountriesData(DataSet):
    """
    Fixture dataset class for Countries
    """
    class accesscontrol_test_country:
        ctry_shortname = 'accestan'
        area = AreasData.accesscontrol_test_area

还有我的模特

class Clubs(db.Model, clubs):
    """Club model class.
    """
    shift_terminal = relationship(
        "Terminals",
        primaryjoin="Terminals.term_id==Clubs.club_terminal_for_shift",
        foreign_keys=[clubs.club_terminal_for_shift],
        backref=backref('shift_terminal_clubs'))

    area = relationship("Areas", backref=backref('clubs'))

...

class Areas(db.Model, areas):
    """Area model class.
    """
    country = relationship("Countries", backref=backref('areas'))

    def __init__(self, *args, **kwargs):
        self.area_id = newid()
        super(Areas, self).__init__(*args, **kwargs)

...

class Countries(db.Model, countries):
    """
    Country model class.
    """
    directdebittype = relationship("DirectdebitTypes", backref=backref('countries'))

    def __init__(self, *args, **kwargs):
        self.ctry_id = newid()
        super(Countries, self).__init__(*args, **kwargs)
...

【问题讨论】:

    标签: python testing sqlalchemy fixtures


    【解决方案1】:

    为什么不删除列定义中的外键,只在关系中添加外键(它不会在数据库中生成键,但效果相同)?把我的一段代码分享给大家。(db.Base是我自己封装的):

    class Post(db.Base):
    
        __tablename__ = 'post'
    
        id = Column(String(32), primary_key=True)
    
    
        category_id = Column(String(32))
        category = relationship('Category', backref='posts',
                               primaryjoin='Category.id==Post.category_id',
                               foreign_keys='Post.category_id')
    

    如您所见,category_id 中没有外键。

    【讨论】:

    • 嗨,屋大维。感谢您的输入;我们有一个基于数据库生成模型的脚本,因为这是一个遗留数据库。如果我们要调整外键,那将对我们的脚本和该应用程序已经存在的代码进行重大更改。这是可行的,但工作量很大。我希望有一个更简单的解决方案,最好是在固定装置本身......
    • ^ ^,好吧,我明白了。我也想从这个问题中得到一个更简单的解决方案。
    【解决方案2】:

    您可以使用fixture 命令从遵循所有外键关系的真实数据库中生成fixture。这在使用fixture 命令一节中有记录。它可能看起来像:

    fixture application.models.Clubs --where id=123 --dsn=<ODBC connection string>
    

    但是,我发现依赖这么多数据通常表明您的应用程序的模块化程度不足以进行测试。更具体地说,您可以在其前面放置一个外观,而不是直接访问 Club 模型,例如 get_club(id)。这将允许您的测试排除get_club 函数并返回一个假对象,而不是接触真正的数据库。这只是一个人为的例子。这个想法是将您的应用程序分成更小的独立部分,这样就可以在不集成这么多更大系统的情况下对其进行测试。

    【讨论】:

    • 感谢库马尔的意见。我明白你在说什么,但你描述的是一个单元测试。固定装置不是特别适合功能测试,其中测试应用程序的大部分功能吗?我们有针对特定功能的单元测试,它们使用模拟对象而不是数据库。但是,我们还需要测试涉及(例如)从数据库中收集和计算数据集的功能。我们想在这种情况下使用夹具。 SQLalchemy 模型似乎强制创建相关表。
    • 当然,数据库固定装置有它们的位置,特别是对于功能测试。功能测试真的很难做到正确,而且我认为它们对于所需的维护量并不总是有用的。您可以采取的另一种方法是编写一个 API,功能测试套件可以使用该 API 在测试之前创建所需的对象——这样您就不会将测试锁定到您的数据库架构中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 2016-05-24
    • 2011-12-05
    • 2012-09-04
    • 1970-01-01
    • 2014-06-23
    • 1970-01-01
    相关资源
    最近更新 更多