使用工厂男孩时如何覆盖模型保存功能?

时间:2017-08-16 12:47:34

标签: python django testing factory-boy

我使用Factory Boy来测试Django项目,并且在测试我已覆盖保存方法的模型时遇到了问题。

模特:

   async def async_subprocess_command(*args):
        # Create subprocess
        process = await asyncio.create_subprocess_exec(
            *args,
            # stdout must a pipe to be accessible as process.stdout
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE)
        # Wait for the subprocess to finish
        stdout, stderr = await process.communicate()

        # Return stdout and sterr
        return stdout.decode().strip(),sterr.decode().strip()

    data1a, data1b = await async_subprocess_command(soxExecutable, "--i", audiofilepath)
    data2a, data2b = await async_subprocess_command(soxExecutable, audiofilepath,"-n", "stat")

工厂:

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))

如果我评论最后一个/第二个&#39; super&#39;测试通过的Profile保存方法中的语句。我想知道这句话是否试图使用相同的ID再次创建配置文件?我尝试了各种各样的事情,比如在Meta类中指定django_get_or_create和各种黑客版本的覆盖工厂的_generation方法,断开并连接后代保存,但我无法让它工作。

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

非常感谢任何帮助。

学家

1 个答案:

答案 0 :(得分:4)

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 ...;]
    • =&GT; self.pk设置为新插入行的pk
  2. 执行自定义代码
  3. 致电super(Profile, self).save(force_insert=True)
    • 这会生成此SQL:INSERT INTO simtrack_profile SET id=N, ...,其中N是对象的pk
    • 显然,发生了崩溃:已经有一行id=N
  4. 您应该修复save()功能,以便第二次拨打super(Profile, self).save() 而不再次重复*args, **kwargs

    注意:

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