工厂男孩错误:ValueError:禁止save()以防止由于未保存的相关对象导致数据丢失

时间:2017-10-05 11:02:42

标签: django django-models django-testing factory-boy

我对factory boy有疑问,通过搜索我发现了一篇帖子,在另一个论坛中描述了我的确切案例,但遗憾的是没有回复。所以我在这里发布了期待回答这个问题:

我的测试失败并显示消息ValueError: save() prohibited to prevent data loss due to unsaved related object 'created_by'我认为与外键有关的问题。

我尝试测试Task模型,这就是我的代码的样子

class User(AbstractUser, Entity):

    middle_name = EnglishNameCharField(_('Middle Name'), max_length=50, blank=True, null=True)
    birth_date = models.DateField(_('Date of Birth'), null=True)
    gender = models.CharField(_('Gender'), max_length=2, choices=GENDER, null=True)
    user_type= models.CharField(max_length=1, verbose_name='user type')
    balance= models.IntegerField(verbose_name='balance', default=0)


    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['first_name', 'last_name', 'email']

    @classmethod
    def hidden_fields(cls):
        fields = super(User, cls).hidden_fields()
        return fields + ('date_joined',
                     'password', 'last_login', 'is_staff',
                     'is_active', 'is_superuser')

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def get_absolute_url(self):
        return "/users/%s/" % urlquote(self.username)

    def get_full_name(self):
    """
    Returns the first_name plus the last_name, with a space in between.
    """
        full_name = '%s %s' % (self.first_name, self.last_name)
        if self.middle_name:
           full_name = '%s %s %s' % (self.first_name, self.middle_name, self.last_name)
        return full_name.strip()

    def save(self, *args, **kwargs):
        if self.first_name:
            self.first_name = " ".join(x.capitalize() for x in self.first_name.split(" "))
        if self.last_name:
            self.last_name = " ".join(x.capitalize() for x in self.last_name.split(" "))
        if self.birth_date:
            self.age = calculate_age(self.birth_date)
        super(User, self).save(*args, **kwargs)


class Task(models.Model):
     title = models.CharField(max_length=255, verbose_name='Заголовок')
     description = models.CharField(max_length=255, verbose_name='Описание')
     cost = models.DecimalField(max_digits=7, decimal_places=2, default=0, verbose_name='Цена')
     assignee = models.ForeignKey('users.User', related_name='assignee', null=True, verbose_name='Исполнитель')
     created_by = models.ForeignKey('users.User', related_name='created_by', verbose_name='Кем был создан')

    def __str__(self):
         return self.title

我用工厂男孩测试它,这是我工厂男生班的样子

class UserFactoryCustomer(factory.Factory):

    class Meta:
        model = User

    first_name = 'name'
    last_name = 'Asadov'
    username = factory.LazyAttribute(lambda o: slugify(o.first_name + '.' + o.last_name))
    email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower())
    user_type = 1
    balance = 10000.00

class UserFactoryExecutor(factory.Factory):

    class Meta:
        model = User

    first_name = 'Uluk'
    last_name = 'Djunusov'
    username = factory.LazyAttribute(lambda o: slugify(o.first_name + '.' + o.last_name))
    email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower())
    user_type = 2
    balance = 5000.00


class TaskFactory(factory.Factory):

    class Meta:
        model = Task

    title = factory.Sequence(lambda n: 'Title {}'.format(n))
    description = factory.Sequence(lambda d: 'Description {}'.format(d))
    cost = 5000.00
    assignee = factory.SubFactory(UserFactoryExecutor)
    created_by = factory.SubFactory(UserFactoryCustomer)

这是我的测试的例子

class ApiModelTestCase(TestCase):

     def test_creating_models_instance(self):
         executor = factories.UserFactoryExecutor()
         customer = factories.UserFactoryCustomer()
         Task.objects.create(title="Simple Task", description="Simple Description", cost="5000.00",
                        assignee=executor, created_by=customer)

这是控制台中显示的错误:

ERROR: test_creating_models_instance (tests.test_models.ApiModelTestCase)

Traceback (most recent call last):
  File "/Users/heartprogrammer/Desktop/freelance-with-api/freelance/tests/test_models.py", line 12, in test_creating_models_instance
  assignee=executor, created_by=customer)
  File "/Users/heartprogrammer/Documents/envs/freelance/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
   File "/Users/heartprogrammer/Documents/envs/freelance/lib/python3.6/site-packages/django/db/models/query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
  File "/Users/heartprogrammer/Documents/envs/freelance/lib/python3.6/site-packages/django/db/models/base.py", line 763, in save
"unsaved related object '%s'." % field.name
 ValueError: save() prohibited to prevent data loss due to unsaved related object 'assignee'.

有什么想法吗?

2 个答案:

答案 0 :(得分:4)

您的所有factory类似乎都来自factory.Factory。例如,我看到:

class UserFactoryCustomer(factory.Factory):

由于您正在测试django模型,因此您应该继承factory.django.DjangoModelFactory。所以您发布的示例现在看起来像:

class UserFactoryCustomer(factory.django.DjangoModelFactory):

class Meta:
    model = User

first_name = 'name'
last_name = 'Asadov'
username = factory.LazyAttribute(lambda o: slugify(o.first_name + '.' + o.last_name))
email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower())
user_type = 1
balance = 10000.00

答案 1 :(得分:0)

我讨厌打破它,但我认为这是错过缩进的问题......

用户的super(User, self).save(*args, **kwargs)方法的save部分实际上并不在Save方法中。这就是为什么它实际上没有被保存。