Factory boy error : ValueError: save() prohibited to prevent data loss due to unsaved related object
Factory boy error : ValueError: save() prohibited to prevent data loss due to unsaved related object
我对 factory boy
有疑问,通过搜索我在另一个论坛中找到了 post 描述我的确切案例,但不幸的是没有回复。所以我 post 在这里编辑它期待对这个问题的回应 :
我的测试失败并显示消息 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就是这样的
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'.
有什么想法吗?
我不想把它打破给你,但我认为这是一个遗漏缩进的问题...
User 的 save
方法的 super(User, self).save(*args, **kwargs)
部分实际上不在 Save 方法中。这就是它实际上没有被保存的原因。
看起来你的所有 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
我对 factory boy
有疑问,通过搜索我在另一个论坛中找到了 post 描述我的确切案例,但不幸的是没有回复。所以我 post 在这里编辑它期待对这个问题的回应 :
我的测试失败并显示消息 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就是这样的
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'.
有什么想法吗?
我不想把它打破给你,但我认为这是一个遗漏缩进的问题...
User 的 save
方法的 super(User, self).save(*args, **kwargs)
部分实际上不在 Save 方法中。这就是它实际上没有被保存的原因。
看起来你的所有 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