【问题标题】:Attribute error from factoryboy for self-referencing SQLAlchemy model来自 factoryboy 的自引用 SQLAlchemy 模型的属性错误
【发布时间】:2019-11-08 10:54:38
【问题描述】:

我意识到之前已经提出过类似的问题 (https://github.com/FactoryBoy/factory_boy/issues/173),但我已经尝试了该票证中的所有建议,但在 SQLAlchemy 模型中创建自引用字段时仍然遇到问题。

具体来说,当我尝试使用这种格式在测试中创建一个 ScaleFactory 对象时(如票证中所建议的那样):

new_scale = factories.ScaleFactory(code=test_code, description_student=test_description_student, parent_scale__parent_scale=None)

我收到以下错误:

  obj = None, name = 'id', default = <class 'factory.declarations._UNSPECIFIED'> . 

  def deepgetattr(obj, name, default=_UNSPECIFIED):
      """Try to retrieve the given attribute of an object, digging on '.'.

      This is an extended getattr, digging deeper if '.' is found.

      Args:
          obj (object): the object of which an attribute should be read
          name (str): the name of an attribute to look up.
          default (object): the default value to use if the attribute wasn't found

      Returns:
          the attribute pointed to by 'name', splitting on '.'.

      Raises:
          AttributeError: if obj has no 'name' attribute.
      """
      try:
          if '.' in name:
              attr, subname = name.split('.', 1)
              return deepgetattr(getattr(obj, attr), subname, default)
          else:
              return getattr(obj, name)
  E           AttributeError: 'NoneType' object has no attribute 'id'

我的工厂模型是这样的:

class ScaleFactory(factory.alchemy.SQLAlchemyModelFactory):
    class Meta:
        model = models.Scale
        sqlalchemy_session = models.orm.session

    id = lazy_attribute(lambda o: fake.uuid4())
    ...
    parent_scale_id = factory.SelfAttribute("parent_scale.id")
    parent_scale = factory.SubFactory('application.factories.ScaleFactory')

我的模型是这样的:

class Scale(orm.Model):
    __tablename__ = "scale"
    id = orm.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    ...
    parent_scale_id = orm.Column(UUID(as_uuid=True), orm.ForeignKey("scale.id"))
    parent_scale = orm.relationship("Scale", remote_side=[id])

我还尝试了有关该支持票的所有其他建议(@factory.post_generation 等),但都导致无限递归。唯一不会产生无限递归的调用是我在此处给出的调用,但这会导致您看到的 AttributeError。很可能我只是不明白我应该在这里传递什么而不是 None 作为 parent_scale__parent_scale。我也尝试过创建一个 MagicMock obj 并将其作为 parent_scale 传递,但这会产生一个 AttributeError 试图找到__name__。所以我完全被困住了!非常感谢任何帮助。

我在 factory-boy==2.12.0

【问题讨论】:

    标签: python sqlalchemy factory-boy


    【解决方案1】:

    要回答我自己的问题,以防万一它对任何人有帮助 - 一种防止无限递归和 AttributeError 的方法是按照原始答案 (https://github.com/FactoryBoy/factory_boy/issues/173) 中的建议传入一个 None 值,但也实现一个post_generation钩子来设置parent_scale_id

    class ScaleFactory(factory.alchemy.SQLAlchemyModelFactory):
        class Meta:
            model = models.Scale
            sqlalchemy_session = models.orm.session
    
        id = lazy_attribute(lambda o: fake.uuid4())
        ...
        parent_scale = factory.SubFactory("application.factories.ScaleFactory")
    
        @factory.post_generation
        def parent_scale_id(self, create, _, **__):
            self.parent_scale_id = self.parent_scale.id if self.parent_scale else None
    

    这样称呼它:

    new_scale = factories.ScaleFactory(code=test_code, description_student=test_description_student, parent_scale__parent_scale__parent_scale__parent_scale=None)                             
    

    这将为您提供一个带有 3 个父 Scales 的 Scale。

    【讨论】:

      猜你喜欢
      • 2018-04-24
      • 2013-04-09
      • 1970-01-01
      • 2019-05-05
      • 1970-01-01
      • 2018-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多