diff --git a/docs/changelog.rst b/docs/changelog.rst index 29475a92..cd1108af 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,11 +3,17 @@ ChangeLog .. Note for v4.x: don't forget to check "Deprecated" sections for removal. -3.3.2 (unreleased) +4.0.0 (unreleased) ------------------ -- Nothing changed yet. +*Removed:* + +- :class:`~factory.django.DjangoModelFactory` no longer issues a second call to + :meth:`~django.db.models.Model.save` on the created instance when :ref:`post-generation-hooks` return a value. + + The transitional setting :attr:`factory.django.DjangoOptions.skip_postgeneration_save` can be removed. +- :func:`factory.use_strategy`. Use :attr:`factory.FactoryOptions.strategy` instead. 3.3.1 (2024-08-18) ------------------ diff --git a/docs/orms.rst b/docs/orms.rst index 7f9d1ef0..26394d54 100644 --- a/docs/orms.rst +++ b/docs/orms.rst @@ -109,13 +109,6 @@ All factories for a Django :class:`~django.db.models.Model` should use the >>> john.email # The email value was not updated "john@example.com" - .. attribute:: skip_postgeneration_save - - Transitional option to prevent :class:`~factory.django.DjangoModelFactory`'s - ``_after_postgeneration`` from issuing a duplicate call to - :meth:`~django.db.models.Model.save` on the created instance when - :class:`factory.PostGeneration` hooks return a value. - Extra fields """""""""""" diff --git a/docs/reference.rst b/docs/reference.rst index 2122b9f7..c7505b0d 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -579,23 +579,6 @@ factory_boy supports two main strategies for generating instances, plus stubs. >>> return obj -.. function:: use_strategy(strategy) - - .. deprecated:: 3.2 - - Use :py:attr:`factory.FactoryOptions.strategy` instead. - - *Decorator* - - Change the default strategy of the decorated :class:`Factory` to the chosen ``strategy``: - - .. code-block:: python - - @use_strategy(factory.BUILD_STRATEGY) - class UserBuildingFactory(UserFactory): - pass - - .. data:: STUB_STRATEGY The 'stub' strategy is an exception in the factory_boy world: it doesn't return diff --git a/factory/__init__.py b/factory/__init__.py index 62042a2a..c1c63bfb 100644 --- a/factory/__init__.py +++ b/factory/__init__.py @@ -9,7 +9,6 @@ Factory, ListFactory, StubFactory, - use_strategy, ) from .declarations import ( ContainerAttribute, diff --git a/factory/base.py b/factory/base.py index 454513be..697ecb20 100644 --- a/factory/base.py +++ b/factory/base.py @@ -727,20 +727,3 @@ def _create(cls, model_class, *args, **kwargs): class ListFactory(BaseListFactory): class Meta: model = list - - -def use_strategy(new_strategy): - """Force the use of a different strategy. - - This is an alternative to setting default_strategy in the class definition. - """ - warnings.warn( - "use_strategy() is deprecated and will be removed in the future.", - DeprecationWarning, - stacklevel=2, - ) - - def wrapped_class(klass): - klass._meta.strategy = new_strategy - return klass - return wrapped_class diff --git a/factory/django.py b/factory/django.py index b53fd5b5..0870786a 100644 --- a/factory/django.py +++ b/factory/django.py @@ -8,7 +8,6 @@ import io import logging import os -import warnings from typing import Dict, TypeVar from django.contrib.auth.hashers import make_password @@ -50,7 +49,6 @@ def _build_default_options(self): return super()._build_default_options() + [ base.OptionDefault('django_get_or_create', (), inherit=True), base.OptionDefault('database', DEFAULT_DB_ALIAS, inherit=True), - base.OptionDefault('skip_postgeneration_save', False, inherit=True), ] def _get_counter_reference(self): @@ -174,23 +172,6 @@ def _create(cls, model_class, *args, **kwargs): manager = cls._get_manager(model_class) return manager.create(*args, **kwargs) - # DEPRECATED. Remove this override with the next major release. - @classmethod - def _after_postgeneration(cls, instance, create, results=None): - """Save again the instance if creating and at least one hook ran.""" - if create and results and not cls._meta.skip_postgeneration_save: - warnings.warn( - f"{cls.__name__}._after_postgeneration will stop saving the instance " - "after postgeneration hooks in the next major release.\n" - "If the save call is extraneous, set skip_postgeneration_save=True " - f"in the {cls.__name__}.Meta.\n" - "To keep saving the instance, move the save call to your " - "postgeneration hooks or override _after_postgeneration.", - DeprecationWarning, - ) - # Some post-generation hooks ran, and may have modified us. - instance.save() - class Password(declarations.Transformer): def __init__(self, password, transform=make_password, **kwargs): diff --git a/tests/test_django.py b/tests/test_django.py index 066d7920..ebc27c8a 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -447,9 +447,6 @@ class PointedRelatedFactory(PointedFactory): factory_related_name='pointed', ) - class Meta: - skip_postgeneration_save = True - class PointerExtraFactory(PointerFactory): pointed__foo = 'extra_new_foo' @@ -466,9 +463,6 @@ class Params: ) ) - class Meta: - skip_postgeneration_save = True - cls.PointedFactory = PointedFactory cls.PointerFactory = PointerFactory cls.PointedRelatedFactory = PointedRelatedFactory @@ -989,7 +983,6 @@ def test_class_decorator_with_subfactory(self): class WithSignalsDecoratedFactory(factory.django.DjangoModelFactory): class Meta: model = models.WithSignals - skip_postgeneration_save = True @factory.post_generation def post(obj, create, extracted, **kwargs): @@ -1017,7 +1010,6 @@ def test_class_decorator_related_model_with_post_hook(self): class PointedFactory(factory.django.DjangoModelFactory): class Meta: model = models.PointedModel - skip_postgeneration_save = True @factory.post_generation def post_action(obj, create, extracted, **kwargs): @@ -1111,7 +1103,6 @@ def test_class_decorator_with_muted_related_factory(self): class UndecoratedFactory(factory.django.DjangoModelFactory): class Meta: model = models.PointerModel - skip_postgeneration_save = True pointed = factory.RelatedFactory(self.WithSignalsDecoratedFactory) UndecoratedFactory() @@ -1134,31 +1125,6 @@ class Meta: ObjFactory.create(arg='invalid') -class DjangoModelFactoryDuplicateSaveDeprecationTest(django_test.TestCase): - class StandardFactoryWithPost(StandardFactory): - @factory.post_generation - def post_action(obj, create, extracted, **kwargs): - return 3 - - def test_create_warning(self): - with self.assertWarns(DeprecationWarning) as cm: - self.StandardFactoryWithPost.create() - - [msg] = cm.warning.args - self.assertEqual( - msg, - "StandardFactoryWithPost._after_postgeneration will stop saving the " - "instance after postgeneration hooks in the next major release.\n" - "If the save call is extraneous, set skip_postgeneration_save=True in the " - "StandardFactoryWithPost.Meta.\n" - "To keep saving the instance, move the save call to your postgeneration " - "hooks or override _after_postgeneration.", - ) - - def test_build_no_warning(self): - self.StandardFactoryWithPost.build() - - class IntegrityErrorForMissingOriginalParamsTest(django_test.TestCase): def test_raises_integrity_error(self):