在模型 save() 中调用 self.full_clean() 时出现问题
Problems when calling self.full_clean() in model save()
我们正在为 Speedy Net and Speedy Match. Recently I found out that django.db.models.Model
save()
method doesn't call full_clean()
. I read about it a little (for example on Why doesn't django's model.save() call full_clean()?) and decided it's a better programming practice to call self.full_clean()
before saving the model. I implemented it with class ValidateModelMixin
(from Django model mixin to force Django to validate (i.e. call full_clean
) before save
使用 Django(目前为 1.11.15):
class ValidateModelMixin(object):
def save(self, *args, **kwargs):
"""Call `full_clean` before saving."""
self.full_clean()
return super().save(*args, **kwargs)
然后在我的基础模型中继承它class(所有模型都继承自它):
class BaseModel(ValidateModelMixin, models.Model):
但问题是,我的大部分测试都失败并显示此消息:
======================================================================
ERROR: test_user_gets_redirected_to_his_profile (speedy.net.accounts.tests.test_views.IndexViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "speedy\net\accounts\tests\test_views.py", line 8, in setUp
self.user = ActiveUserFactory()
File "VENV~1\lib\site-packages\factory\base.py", line 46, in __call__
return cls.create(**kwargs)
File "VENV~1\lib\site-packages\factory\base.py", line 563, in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
File "VENV~1\lib\site-packages\factory\base.py", line 500, in _generate
return step.build()
File "VENV~1\lib\site-packages\factory\builder.py", line 279, in build
kwargs=kwargs,
File "VENV~1\lib\site-packages\factory\base.py", line 314, in instantiate
return self.factory._create(model, *args, **kwargs)
File "VENV~1\lib\site-packages\factory\django.py", line 165, in _create
return manager.create(*args, **kwargs)
File "VENV~1\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "VENV~1\lib\site-packages\modeltranslation\manager.py", line 405, in create
return super(MultilingualQuerySet, self).create(**kwargs)
File "VENV~1\lib\site-packages\django\db\models\query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
File "speedy\core\accounts\models.py", line 52, in save
return super().save(*args, **kwargs)
File "speedy\core\base\models.py", line 27, in save
return super().save(*args, **kwargs)
File "speedy\core\base\models.py", line 12, in save
self.full_clean()
File "VENV~1\lib\site-packages\django\db\models\base.py", line 1250, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'password': ['This field cannot be blank.']}
----------------------------------------------------------------------
我认为问题出在我们定义的 class 上,特别是我们定义密码的方式:
class DefaultUserFactory(factory.DjangoModelFactory):
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
date_of_birth = factory.fuzzy.FuzzyDate(start_date=date(year=1900, month=1, day=1))
gender = User.GENDER_OTHER
slug = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
username = factory.LazyAttribute(lambda o: normalize_username(slug=o.slug))
password = factory.PostGenerationMethodCall(method_name='set_password', raw_password=USER_PASSWORD)
class Meta:
model = User
是否有更好的方法来定义此 class 以便测试能够通过?
我通过这样定义 class DefaultUserFactory
找到了解决方案:
class DefaultUserFactory(factory.DjangoModelFactory):
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
date_of_birth = factory.fuzzy.FuzzyDate(start_date=date(year=1900, month=1, day=1))
gender = User.GENDER_OTHER
slug = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
username = factory.LazyAttribute(lambda o: normalize_username(slug=o.slug))
password = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
_password = factory.PostGenerationMethodCall(method_name='set_password', raw_password=USER_PASSWORD)
class Meta:
model = User
我们正在为 Speedy Net and Speedy Match. Recently I found out that django.db.models.Model
save()
method doesn't call full_clean()
. I read about it a little (for example on Why doesn't django's model.save() call full_clean()?) and decided it's a better programming practice to call self.full_clean()
before saving the model. I implemented it with class ValidateModelMixin
(from Django model mixin to force Django to validate (i.e. call full_clean
) before save
使用 Django(目前为 1.11.15):
class ValidateModelMixin(object):
def save(self, *args, **kwargs):
"""Call `full_clean` before saving."""
self.full_clean()
return super().save(*args, **kwargs)
然后在我的基础模型中继承它class(所有模型都继承自它):
class BaseModel(ValidateModelMixin, models.Model):
但问题是,我的大部分测试都失败并显示此消息:
======================================================================
ERROR: test_user_gets_redirected_to_his_profile (speedy.net.accounts.tests.test_views.IndexViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "speedy\net\accounts\tests\test_views.py", line 8, in setUp
self.user = ActiveUserFactory()
File "VENV~1\lib\site-packages\factory\base.py", line 46, in __call__
return cls.create(**kwargs)
File "VENV~1\lib\site-packages\factory\base.py", line 563, in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
File "VENV~1\lib\site-packages\factory\base.py", line 500, in _generate
return step.build()
File "VENV~1\lib\site-packages\factory\builder.py", line 279, in build
kwargs=kwargs,
File "VENV~1\lib\site-packages\factory\base.py", line 314, in instantiate
return self.factory._create(model, *args, **kwargs)
File "VENV~1\lib\site-packages\factory\django.py", line 165, in _create
return manager.create(*args, **kwargs)
File "VENV~1\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "VENV~1\lib\site-packages\modeltranslation\manager.py", line 405, in create
return super(MultilingualQuerySet, self).create(**kwargs)
File "VENV~1\lib\site-packages\django\db\models\query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
File "speedy\core\accounts\models.py", line 52, in save
return super().save(*args, **kwargs)
File "speedy\core\base\models.py", line 27, in save
return super().save(*args, **kwargs)
File "speedy\core\base\models.py", line 12, in save
self.full_clean()
File "VENV~1\lib\site-packages\django\db\models\base.py", line 1250, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'password': ['This field cannot be blank.']}
----------------------------------------------------------------------
我认为问题出在我们定义的 class 上,特别是我们定义密码的方式:
class DefaultUserFactory(factory.DjangoModelFactory):
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
date_of_birth = factory.fuzzy.FuzzyDate(start_date=date(year=1900, month=1, day=1))
gender = User.GENDER_OTHER
slug = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
username = factory.LazyAttribute(lambda o: normalize_username(slug=o.slug))
password = factory.PostGenerationMethodCall(method_name='set_password', raw_password=USER_PASSWORD)
class Meta:
model = User
是否有更好的方法来定义此 class 以便测试能够通过?
我通过这样定义 class DefaultUserFactory
找到了解决方案:
class DefaultUserFactory(factory.DjangoModelFactory):
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
date_of_birth = factory.fuzzy.FuzzyDate(start_date=date(year=1900, month=1, day=1))
gender = User.GENDER_OTHER
slug = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
username = factory.LazyAttribute(lambda o: normalize_username(slug=o.slug))
password = factory.fuzzy.FuzzyText(chars=string.ascii_lowercase)
_password = factory.PostGenerationMethodCall(method_name='set_password', raw_password=USER_PASSWORD)
class Meta:
model = User