Как переопределить функцию сохранения модели при использовании фабричного мальчика?
Я использую Factory Boy для тестирования проекта Django, и у меня возникла проблема при тестировании модели, для которой я переопределил метод сохранения.
Модель:
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
Если я закомментирую последний последний / второй оператор 'super' в методе сохранения профиля, тесты пройдут. Интересно, пытается ли это утверждение снова создать профиль с тем же идентификатором? Я пробовал разные вещи, такие как указание в мета-классе django_get_or_create и различные взломанные версии переопределения метода _generation для Factory с отключением и подключением сохранения после генерации, но я не могу заставить его работать.
Тем временем я установил стратегию построения, но очевидно, что это не будет проверять мой метод сохранения.
Любая помощь с благодарностью.
J.
1 ответ
factory_boy
использует MyModel.objects.create()
функция из ORM Джанго.
Эта функция вызывает obj.save(force_insert=True)
: https://github.com/django/django/blob/master/django/db/models/query.py
С твоим перегруженным save()
функция, это означает, что вы получите:
- Вызов
super(Profile, self).save(force_insert=True)
- [SQL:
INSERT INTO simtrack_profile SET ...;
] - =>
self.pk
устанавливается на рк вновь вставленной строки
- [SQL:
- Выполните ваш пользовательский код
- Вызов
super(Profile, self).save(force_insert=True)
- Это генерирует этот SQL:
INSERT INTO simtrack_profile SET id=N, ...
, сN
быть ПК объекта - Очевидно, происходит сбой: уже есть строка с
id=N
,
- Это генерирует этот SQL:
Вы должны исправить save()
функция, так что во второй раз вы звоните super(Profile, self).save()
без повторения *args, **kwargs
снова.
Заметки:
- Ваш код сломается, когда вы добавите объект через администратора Django, или когда вы будете использовать
Profile.objects.create()
, - Так как вы не модифицируете
self
в твоем перегруженномsave()
функция, вы должны быть в состоянии удалить второй вызовsuper(Profile, self).save()
в целом; хотя сохранение его может быть полезным, чтобы избежать странных ошибок, если вам нужно добавить больше пользовательского поведения позже.