【问题标题】:How to overriding model save function when using factory boy?使用工厂男孩时如何覆盖模型保存功能?
【发布时间】:2017-08-16 12:47:33
【问题描述】:

我正在使用Factory Boy 测试一个 Django 项目,但在测试一个我已经覆盖了 save 方法的模型时遇到了问题。

型号:

class Profile(models.Model):

    active = models.BooleanField()
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,
                             related_name='profiles')
    department = models.ForeignKey(Department, null=True, blank=True)
    category_at_start = models.ForeignKey(Category)
    role = models.ForeignKey(Role)
    series = models.ForeignKey(Series, null=True, blank=True)
    status = models.ForeignKey('Status', Status)

    def save(self, *args, **kwargs):
        super(Profile, self).save(*args, **kwargs)
        active_roles = []
        active_status = []
        for profile in Profile.objects.filter(user=self.user):
            if profile.active:
                active_roles.append(profile.role.code)
                active_status.append(profile.status.name)
        self.user.current_role = '/'.join(set(active_roles))
        if 'Training' in active_status:
            self.user.current_status = 'Training'
        elif 'Certified' in active_status:
            self.user.current_status = 'Certified'
        else:
            self.user.current_status = '/'.join(set(active_status))
        self.user.save()
        super(Profile, self).save(*args, **kwargs) ### <-- seems to be the issue.

工厂:

class ProfileFactory(f.django.DjangoModelFactory):
    class Meta:
        model = models.Profile

    active = f.Faker('boolean')
    user = f.SubFactory(UserFactory)
    department = f.SubFactory(DepartmentFactory)
    category_at_start = f.SubFactory(CategoryFactory)
    role = f.SubFactory(RoleFactory)
    series = f.SubFactory(SeriesFactory)
    status = f.SubFactory(StatusFactory)

测试:

class ProfileTest(TestCase):

    def test_profile_creation(self):
        o = factories.ProfileFactory()
        self.assertTrue(isinstance(o, models.Profile))

当我运行测试时,我收到以下错误:

django.db.utils.IntegrityError: UNIQUE constraint failed: simtrack_profile.id

如果我在 Profile 保存方法中注释掉最后一个/第二个“超级”语句,则测试通过。我想知道这个语句是否正在尝试使用相同的 ID 再次创建配置文件?我尝试了各种方法,例如在 Meta 类 django_get_or_create 中指定以及通过断开和连接后生成保存来覆盖工厂的 _generation 方法的各种黑客版本,但我无法让它工作。

与此同时,我已经设置了构建策略,但显然这不会测试我的保存方法。

非常感谢任何帮助。

J.

【问题讨论】:

    标签: python django testing factory-boy


    【解决方案1】:

    factory_boy 使用 Django 的 ORM 中的 MyModel.objects.create() 函数。

    该函数调用obj.save(force_insert=True)https://github.com/django/django/blob/master/django/db/models/query.py#L384

    使用重载的save() 函数,这意味着您得到:

    1. 致电super(Profile, self).save(force_insert=True)
      • [SQL:INSERT INTO simtrack_profile SET ...;]
      • => self.pk 设置为新插入行的pk
    2. 执行您的自定义代码
    3. 致电super(Profile, self).save(force_insert=True)
      • 这会生成以下 SQL:INSERT INTO simtrack_profile SET id=N, ...,其中 N 是对象的 pk
      • 很明显,发生了崩溃:已经有一行id=N

    您应该修复您的save() 函数,以便您第二次调用super(Profile, self).save() 而不再次重复*args, **kwargs

    注意事项:

    • 当您通过 Django 的管理员添加对象时,或者您使用 Profile.objects.create() 的任何时候,您的代码都会中断。
    • 由于您没有在重载的save() 函数中修改self,因此您应该能够完全删除对super(Profile, self).save() 的第二次调用;虽然如果您以后需要添加更多自定义行为,保留它可能有助于避免奇怪的错误。

    【讨论】:

    • 从第二次保存中移除 *args, **kwargs 效果很好!显然没有点击我没有在保存重载中编辑自我(而是 self.user)。非常感谢。并感谢您详细解释了保存超载的情况。作为记录,它既可以通过管理员创建对象,也可以通过 python shell 中的 objects.create() 创建对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2018-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    相关资源
    最近更新 更多