'Factory boy error : ValueError: save() prohibited to prevent data loss due to unsaved related object
I have a problem with factory boy, by searching I've found a post describing my exact case in another forum , but unfortunately with no responses. So I posted it here looking forward to have a response to this issue :
My test fails with the message ValueError: save() prohibited to prevent data loss due to unsaved related object 'created_by' I thinking the problem in related to foreign key.
I try to test Task model, this is how my code looks like
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
I test it with factory boy that is how my factory boy class looks like
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)
This is the example of my test
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)
This is the error shown in the console:
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'.
Any ideas ?
Solution 1:[1]
I hate to break it to you, but I think this is a problem of missed indentation...
The super(User, self).save(*args, **kwargs) part of your save method for User isn't actually in the Save method. That's why it's not actually being saved.
Solution 2:[2]
I just uninstalled Json extension package from my pip list and it worked without any mentioned error.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Sam Bobel |
| Solution 2 | Pari |
