Ошибка атрибута из factoryboy для самореферентной модели SQLAlchemy - PullRequest
0 голосов
/ 08 ноября 2019

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

Я на фабрике == 2.12.0

1 Ответ

0 голосов
/ 08 ноября 2019

Чтобы ответить на мой собственный вопрос, в случае, если это кому-нибудь поможет - способ сделать это, чтобы предотвратить как бесконечную рекурсию, так и 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 родительскими шкалами.

...