Ошибка атрибута от factoryboy для модели SQLAlchemy, ссылающейся на себя

Я понимаю, что подобный вопрос уже задавался раньше (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 и передать его как parent_scale, но это привело к ошибке AttributeError, пытающейся найти __name__. Так что я полностью застрял! Любая помощь очень ценится.

У меня фабричный мальчик == 2.12.0


person davefelce    schedule 08.11.2019    source источник


Ответы (1)


Чтобы ответить на мой собственный вопрос, на случай, если это кому-нибудь поможет, способ сделать это, чтобы предотвратить как бесконечную рекурсию, так и AttributeError, состоит в том, чтобы передать значение None, как это было предложено в исходном ответе (https://github.com/FactoryBoy/factory_boy/issues/173), но также реализовать хук 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 родительскими шкалами.

person davefelce    schedule 08.11.2019