From c347e8bb3c86a53ae70ea4d240b624a65c5a11c5 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Fri, 24 Mar 2023 11:52:09 +0100 Subject: [PATCH 01/10] Deprecate StateLog model, to instead bring your own model. This is an implementation that support the idea exposed in https://github.com/jazzband/django-fsm-log/discussions/133 It's still a Draft, I'm open for any feedback more specifically around the names. Not tested yet with a real project. It's meant to be completely backward compatible. --- README.md | 58 ++++++++++++++----- django_fsm_log/admin.py | 19 ++++-- django_fsm_log/apps.py | 4 +- django_fsm_log/backends.py | 27 ++++++--- django_fsm_log/conf.py | 9 ++- django_fsm_log/managers.py | 25 ++++++-- django_fsm_log/migrations/0001_initial.py | 2 +- .../migrations/0004_auto_20190131_0341.py | 1 + django_fsm_log/models.py | 29 ++++++++-- tests/admin.py | 4 +- tests/conftest.py | 10 ++-- tests/models.py | 5 ++ tests/settings.py | 2 + tests/test_deprecations.py | 29 ++++++++++ tests/test_manager.py | 32 +++++----- tests/test_model.py | 44 +++++++------- tests/test_settings.py | 10 ++-- 17 files changed, 224 insertions(+), 86 deletions(-) create mode 100644 tests/test_deprecations.py diff --git a/README.md b/README.md index 69d724a..f552a74 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ by enabling a cached backend. See [Advanced Usage](#advanced-usage) ## 4.0.0 (not released) - remove support for django 2.2 & 4.0 +- Bring your own PersistedTransition model: + From this relase, django-fsm-log is deprecating StateLog and instead encourages you + to define for your own application the concrete model that will persist the transition ## 3.1.0 (2023-03-23) @@ -99,14 +102,29 @@ python manage.py migrate django_fsm_log ## Usage +### Define you own model + +```python +from django_fsm_log.models import PersistedTransitionMixin + + +class TransitionLog(PersistedTransitionMixin): + pass +``` + +### Register the model + +```python +DJANGO_FSM_LOG_CONCRETE_MODEL = 'poll.models.TransitionLog' # This model must inherit from django_fsm_log.models.PersistedTransition +``` + The app listens for the `django_fsm.signals.post_transition` signal and creates a new record for each transition. To query the log: ```python -from django_fsm_log.models import StateLog -StateLog.objects.all() +TransitionLog.objects.all() # ...all recorded logs... ``` @@ -125,11 +143,10 @@ For convenience there is a custom `for_` manager method to easily filter on the ```python from my_app.models import Article -from django_fsm_log.models import StateLog article = Article.objects.all()[0] -StateLog.objects.for_(article) +TransitionLog.objects.for_(article) # ...logs for article... ``` @@ -157,7 +174,7 @@ With this the transition gets logged when the `by` kwarg is present. ```python article = Article.objects.create() -article.submit(by=some_user) # StateLog.by will be some_user +article.submit(by=some_user) # TransitionLog.by will be some_user ``` ### `description` Decorator @@ -210,21 +227,31 @@ article.submit() # logged with "Article submitted" description There is an InlineForm available that can be used to display the history of changes. -To use it expand your own `AdminModel` by adding `StateLogInline` to its inlines: +To use it expand your own `AdminModel` by adding `PersistedTransitionInline` to its inlines: ```python from django.contrib import admin -from django_fsm_log.admin import StateLogInline +from django_fsm_log.admin import PersistedTransitionInline @admin.register(FSMModel) class FSMModelAdmin(admin.ModelAdmin): - inlines = [StateLogInline] + inlines = [PersistedTransitionInline] +``` + +### Migration to Abstract model PersistedTransitionMixin + +Once you defined your own model, you'll have to create the relevant migration to create the table. + +```sh +python manage.py makemigrations ``` +Additionally you'd want to migrate the data from django_fsm_log.models.StateLog to your new table. + ### Advanced Usage -You can change the behaviour of this app by turning on caching for StateLog records. +You can change the behaviour of this app by turning on caching for PersistedTransition records. Simply add `DJANGO_FSM_LOG_STORAGE_METHOD = 'django_fsm_log.backends.CachedBackend'` to your project's settings file. It will use your project's default cache backend by default. If you wish to use a specific cache backend, you can add to your project's settings: @@ -233,22 +260,23 @@ your project's settings: DJANGO_FSM_LOG_CACHE_BACKEND = 'some_other_cache_backend' ``` -The StateLog object is now available after the `django_fsm.signals.pre_transition` +The PersistedTransition object is now available after the `django_fsm.signals.pre_transition` signal is fired, but is deleted from the cache and persisted to the database after `django_fsm.signals.post_transition` is fired. This is useful if: -- you need immediate access to StateLog details, and cannot wait until `django_fsm.signals.post_transition` +- you need immediate access to PersistedTransition details, and cannot wait until `django_fsm.signals.post_transition` has been fired -- at any stage, you need to verify whether or not the StateLog has been written to the database +- at any stage, you need to verify whether or not the PersistedTransition has been written to the database -Access to the pending StateLog record is available via the `pending_objects` manager +Access to the pending PersistedTransition record is available via the `pending_objects` manager ```python -from django_fsm_log.models import StateLog +from my_app.models import TransitionLog + article = Article.objects.get(...) -pending_state_log = StateLog.pending_objects.get_for_object(article) +pending_transition_logs = TransitionLog.pending_objects.get_for_object(article) ``` ## Contributing diff --git a/django_fsm_log/admin.py b/django_fsm_log/admin.py index 648e363..9acd9e6 100644 --- a/django_fsm_log/admin.py +++ b/django_fsm_log/admin.py @@ -1,13 +1,15 @@ +from warnings import warn + from django.contrib.contenttypes.admin import GenericTabularInline from django.db.models import F -from .models import StateLog +from .backends import _get_concrete_model -__all__ = ("StateLogInline",) +__all__ = ("PersistedTransitionInline",) -class StateLogInline(GenericTabularInline): - model = StateLog +class PersistedTransitionInline(GenericTabularInline): + model = _get_concrete_model() can_delete = False def has_add_permission(self, request, obj=None): @@ -30,3 +32,12 @@ def get_readonly_fields(self, request, obj=None): def get_queryset(self, request): return super().get_queryset(request).order_by(F("timestamp").desc()) + + +def StateLogInline(*args, **kwargs): + warn( + "StateLogInLine has been deprecated by PersistedTransitionInline.", + DeprecationWarning, + stacklevel=2, + ) + return PersistedTransitionInline(*args, **kwargs) diff --git a/django_fsm_log/apps.py b/django_fsm_log/apps.py index 50324ea..29d9417 100644 --- a/django_fsm_log/apps.py +++ b/django_fsm_log/apps.py @@ -10,9 +10,9 @@ class DjangoFSMLogAppConfig(AppConfig): def ready(self): backend = import_string(settings.DJANGO_FSM_LOG_STORAGE_METHOD) - StateLog = self.get_model("StateLog") + ConcreteModel = import_string(settings.DJANGO_FSM_LOG_CONCRETE_MODEL) - backend.setup_model(StateLog) + backend.setup_model(ConcreteModel) pre_transition.connect(backend.pre_transition_callback) post_transition.connect(backend.post_transition_callback) diff --git a/django_fsm_log/backends.py b/django_fsm_log/backends.py index fe4dfeb..94ce31e 100644 --- a/django_fsm_log/backends.py +++ b/django_fsm_log/backends.py @@ -1,7 +1,18 @@ +import typing + +from django.utils.module_loading import import_string + from django_fsm_log.conf import settings from .helpers import FSMLogDescriptor +if typing.TYPE_CHECKING: + import django_fsm_log.models + + +def _get_concrete_model() -> typing.Type["django_fsm_log.models.PersistedTransitionMixin"]: + return import_string(settings.DJANGO_FSM_LOG_CONCRETE_MODEL) + def _pre_transition_callback(sender, instance, name, source, target, manager, **kwargs): if BaseBackend._get_model_qualified_name__(sender) in settings.DJANGO_FSM_LOG_IGNORED_MODELS: @@ -49,21 +60,21 @@ def _get_model_qualified_name__(sender): class CachedBackend(BaseBackend): @staticmethod def setup_model(model): - from .managers import PendingStateLogManager + from .managers import PendingPersistedTransitionManager - model.add_to_class("pending_objects", PendingStateLogManager()) + model.add_to_class("pending_objects", PendingPersistedTransitionManager()) @staticmethod def pre_transition_callback(sender, instance, name, source, target, **kwargs): - from .models import StateLog + klass = _get_concrete_model() - return _pre_transition_callback(sender, instance, name, source, target, StateLog.pending_objects, **kwargs) + return _pre_transition_callback(sender, instance, name, source, target, klass.pending_objects, **kwargs) @staticmethod def post_transition_callback(sender, instance, name, source, target, **kwargs): - from .models import StateLog + klass = _get_concrete_model() - StateLog.pending_objects.commit_for_object(instance) + klass.pending_objects.commit_for_object(instance) class SimpleBackend(BaseBackend): @@ -77,9 +88,9 @@ def pre_transition_callback(sender, **kwargs): @staticmethod def post_transition_callback(sender, instance, name, source, target, **kwargs): - from .models import StateLog + klass = _get_concrete_model() - return _pre_transition_callback(sender, instance, name, source, target, StateLog.objects, **kwargs) + return _pre_transition_callback(sender, instance, name, source, target, klass.objects, **kwargs) if settings.DJANGO_FSM_LOG_STORAGE_METHOD == "django_fsm_log.backends.CachedBackend": diff --git a/django_fsm_log/conf.py b/django_fsm_log/conf.py index 144df77..37f7cdb 100644 --- a/django_fsm_log/conf.py +++ b/django_fsm_log/conf.py @@ -1,3 +1,5 @@ +from typing import List + from appconf import AppConf from django.conf import settings # noqa: F401 @@ -5,4 +7,9 @@ class DjangoFSMLogConf(AppConf): STORAGE_METHOD = "django_fsm_log.backends.SimpleBackend" CACHE_BACKEND = "default" - IGNORED_MODELS = [] + IGNORED_MODELS: List[str] = [] + CONCRETE_MODEL: str = "django_fsm_log.models.StateLog" + + class Meta: + prefix = "django_fsm_log" + holder = "django_fsm_log.conf.settings" diff --git a/django_fsm_log/managers.py b/django_fsm_log/managers.py index d3fd057..c3c238c 100644 --- a/django_fsm_log/managers.py +++ b/django_fsm_log/managers.py @@ -1,3 +1,5 @@ +from warnings import warn + from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models.query import QuerySet @@ -5,7 +7,7 @@ from django_fsm_log.backends import cache -class StateLogQuerySet(QuerySet): +class PersistedTransitionQuerySet(QuerySet): def _get_content_type(self, obj): return ContentType.objects.get_for_model(obj) @@ -13,11 +15,16 @@ def for_(self, obj): return self.filter(content_type=self._get_content_type(obj), object_id=obj.pk) -class StateLogManager(models.Manager): +def StateLogQuerySet(*args, **kwargs): + warn("StateLogQuerySet has been renamed PersistedTransitionQuerySet", DeprecationWarning, stacklevel=2) + return PersistedTransitionQuerySet(*args, **kwargs) + + +class PersistedTransitionManager(models.Manager): use_in_migrations = True def get_queryset(self): - return StateLogQuerySet(self.model) + return PersistedTransitionQuerySet(self.model) def __getattr__(self, attr, *args): # see https://code.djangoproject.com/ticket/15062 for details @@ -26,7 +33,12 @@ def __getattr__(self, attr, *args): return getattr(self.get_queryset(), attr, *args) -class PendingStateLogManager(models.Manager): +def StateLogManager(*args, **kwargs): + warn("StateLogManager has been renamed PersistedTransitionManager", DeprecationWarning, stacklevel=2) + return PersistedTransitionManager(*args, **kwargs) + + +class PendingPersistedTransitionManager(models.Manager): def _get_cache_key_for_object(self, obj): return f"StateLog:{obj.__class__.__name__}:{obj.pk}" @@ -46,3 +58,8 @@ def commit_for_object(self, obj): def get_for_object(self, obj): key = self._get_cache_key_for_object(obj) return cache.get(key) + + +def PendingStateLogManager(*args, **kwargs): + warn("PendingStateLogManager has been renamed PendingPersistedTransitionManager", DeprecationWarning, stacklevel=2) + return PendingPersistedTransitionManager(*args, **kwargs) diff --git a/django_fsm_log/migrations/0001_initial.py b/django_fsm_log/migrations/0001_initial.py index 933e877..f7a2bc0 100644 --- a/django_fsm_log/migrations/0001_initial.py +++ b/django_fsm_log/migrations/0001_initial.py @@ -1,6 +1,6 @@ -from django.db import models, migrations import django.utils.timezone from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/django_fsm_log/migrations/0004_auto_20190131_0341.py b/django_fsm_log/migrations/0004_auto_20190131_0341.py index 5e65275..9fdce93 100644 --- a/django_fsm_log/migrations/0004_auto_20190131_0341.py +++ b/django_fsm_log/migrations/0004_auto_20190131_0341.py @@ -1,6 +1,7 @@ # Generated by Django 2.1.5 on 2019-01-31 03:41 from django.db import migrations + import django_fsm_log.managers diff --git a/django_fsm_log/models.py b/django_fsm_log/models.py index fc13d8e..b5fe08d 100644 --- a/django_fsm_log/models.py +++ b/django_fsm_log/models.py @@ -1,14 +1,22 @@ +from warnings import warn + +from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.timezone import now from django_fsm import FSMFieldMixin, FSMIntegerField -from .conf import settings -from .managers import StateLogManager +from .managers import PersistedTransitionManager + +class PersistedTransitionMixin(models.Model): + """ + Abstract Class that should be subclassed by the host application. + Host projects should own the migrations and + can decide on their own primary key type. + """ -class StateLog(models.Model): timestamp = models.DateTimeField(default=now) by = models.ForeignKey( getattr(settings, "AUTH_USER_MODEL", "auth.User"), @@ -26,9 +34,10 @@ class StateLog(models.Model): description = models.TextField(blank=True, null=True) - objects = StateLogManager() + objects = PersistedTransitionManager() class Meta: + abstract = True get_latest_by = "timestamp" def __str__(self): @@ -47,3 +56,15 @@ def get_state_display(self, field_name="state"): def get_source_state_display(self): return self.get_state_display("source_state") + + +class StateLog(PersistedTransitionMixin): + def __init__(self, *args, **kwargs): + warn( + "StateLog model has been deprecated, you should now bring your own Model." + "\nPlease check the documentation at https://django-fsm-log.readthedocs.io/en/latest/" + "\nto know how to.", + DeprecationWarning, + stacklevel=2, + ) + return super().__init__(*args, **kwargs) diff --git a/tests/admin.py b/tests/admin.py index 9e240ac..3db0b53 100644 --- a/tests/admin.py +++ b/tests/admin.py @@ -1,10 +1,10 @@ from django.contrib import admin -from django_fsm_log.admin import StateLogInline +from django_fsm_log.admin import PersistedTransitionInline from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): - inlines = [StateLogInline] + inlines = [PersistedTransitionInline] diff --git a/tests/conftest.py b/tests/conftest.py index 36f19b2..4515383 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,9 @@ import pytest from django.contrib.auth import get_user_model +from django.utils.module_loading import import_string -from django_fsm_log.managers import PendingStateLogManager -from django_fsm_log.models import StateLog +from django_fsm_log.conf import settings +from django_fsm_log.managers import PendingPersistedTransitionManager from .models import Article, ArticleInteger @@ -33,5 +34,6 @@ def user(db): @pytest.fixture(autouse=True) def pending_objects(db, request): if "pending_objects" in request.keywords: - if not hasattr(StateLog, "pending_objects"): - StateLog.add_to_class("pending_objects", PendingStateLogManager()) + model_class = import_string(settings.DJANGO_FSM_LOG_CONCRETE_MODEL) + if not hasattr(model_class, "pending_objects"): + model_class.add_to_class("pending_objects", PendingPersistedTransitionManager()) diff --git a/tests/models.py b/tests/models.py index 2a1698b..44c7473 100644 --- a/tests/models.py +++ b/tests/models.py @@ -2,6 +2,11 @@ from django_fsm import FSMField, FSMIntegerField, transition from django_fsm_log.decorators import fsm_log_by, fsm_log_description +from django_fsm_log.models import PersistedTransitionMixin + + +class PersistedTransition(PersistedTransitionMixin): + pass class Article(models.Model): diff --git a/tests/settings.py b/tests/settings.py index 1878b10..ae2f07e 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -34,3 +34,5 @@ ] USE_TZ = True + +DJANGO_FSM_LOG_CONCRETE_MODEL = "tests.models.PersistedTransition" diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py new file mode 100644 index 0000000..b59d524 --- /dev/null +++ b/tests/test_deprecations.py @@ -0,0 +1,29 @@ +import pytest + + +def test_state_log_model(): + from django_fsm_log.models import StateLog + + with pytest.deprecated_call(): + StateLog() + + +def test_state_log_queryset(): + from django_fsm_log.managers import StateLogQuerySet + + with pytest.deprecated_call(): + StateLogQuerySet() + + +def test_state_log_manager(): + from django_fsm_log.managers import StateLogManager + + with pytest.deprecated_call(): + StateLogManager() + + +def test_pending_state_log_manager(): + from django_fsm_log.managers import PendingStateLogManager + + with pytest.deprecated_call(): + PendingStateLogManager() diff --git a/tests/test_manager.py b/tests/test_manager.py index 33f107a..7aa4d33 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1,6 +1,8 @@ import pytest -from django_fsm_log.models import StateLog +from django_fsm_log.backends import _get_concrete_model + +ConcreteModel = _get_concrete_model() def test_for_queryset_method_returns_only_logs_for_provided_object(article, article2): @@ -9,8 +11,8 @@ def test_for_queryset_method_returns_only_logs_for_provided_object(article, arti article.submit() article.publish() - assert len(StateLog.objects.for_(article)) == 2 - for log in StateLog.objects.for_(article): + assert len(ConcreteModel.objects.for_(article)) == 2 + for log in ConcreteModel.objects.for_(article): assert article == log.content_object @@ -27,15 +29,15 @@ def create_kwargs(user, article): @pytest.mark.pending_objects def test_get_cache_key_for_object_returns_correctly_formatted_string(article): expected_result = f"StateLog:{article.__class__.__name__}:{article.pk}" - result = StateLog.pending_objects._get_cache_key_for_object(article) + result = ConcreteModel.pending_objects._get_cache_key_for_object(article) assert result == expected_result @pytest.mark.pending_objects def test_create_pending_sets_cache_item(article, create_kwargs, mocker): mock_cache = mocker.patch("django_fsm_log.managers.cache") - expected_cache_key = StateLog.pending_objects._get_cache_key_for_object(article) - StateLog.pending_objects.create(**create_kwargs) + expected_cache_key = ConcreteModel.pending_objects._get_cache_key_for_object(article) + ConcreteModel.pending_objects.create(**create_kwargs) cache_key = mock_cache.set.call_args_list[0][0][0] cache_object = mock_cache.set.call_args_list[0][0][1] assert cache_key == expected_cache_key @@ -48,7 +50,7 @@ def test_create_pending_sets_cache_item(article, create_kwargs, mocker): @pytest.mark.pending_objects def test_create_returns_correct_state_log(mocker, create_kwargs): mocker.patch("django_fsm_log.managers.cache") - log = StateLog.pending_objects.create(**create_kwargs) + log = ConcreteModel.pending_objects.create(**create_kwargs) assert log.state == create_kwargs["state"] assert log.transition == create_kwargs["transition"] assert log.content_object == create_kwargs["content_object"] @@ -58,10 +60,10 @@ def test_create_returns_correct_state_log(mocker, create_kwargs): @pytest.mark.pending_objects def test_commit_for_object_saves_log(mocker, article, create_kwargs): mock_cache = mocker.patch("django_fsm_log.managers.cache") - log = StateLog.objects.create(**create_kwargs) + log = ConcreteModel.objects.create(**create_kwargs) mock_cache.get.return_value = log - StateLog.pending_objects.commit_for_object(article) - persisted_log = StateLog.objects.order_by("-pk").all()[0] + ConcreteModel.pending_objects.commit_for_object(article) + persisted_log = ConcreteModel.objects.order_by("-pk").all()[0] assert log.state == persisted_log.state assert log.transition == persisted_log.transition assert log.content_object == persisted_log.content_object @@ -71,14 +73,14 @@ def test_commit_for_object_saves_log(mocker, article, create_kwargs): @pytest.mark.pending_objects def test_commit_for_object_deletes_pending_log_from_cache(mocker, article, create_kwargs): mock_cache = mocker.patch("django_fsm_log.managers.cache") - StateLog.pending_objects.create(**create_kwargs) - StateLog.pending_objects.commit_for_object(article) - mock_cache.delete.assert_called_once_with(StateLog.pending_objects._get_cache_key_for_object(article)) + ConcreteModel.pending_objects.create(**create_kwargs) + ConcreteModel.pending_objects.commit_for_object(article) + mock_cache.delete.assert_called_once_with(ConcreteModel.pending_objects._get_cache_key_for_object(article)) @pytest.mark.pending_objects def test_get_for_object_calls_cache_get_with_correct_key(mocker, create_kwargs): mock_cache = mocker.patch("django_fsm_log.managers.cache") - cache_key = StateLog.pending_objects._get_cache_key_for_object(create_kwargs["content_object"]) - StateLog.pending_objects.get_for_object(create_kwargs["content_object"]) + cache_key = ConcreteModel.pending_objects._get_cache_key_for_object(create_kwargs["content_object"]) + ConcreteModel.pending_objects.get_for_object(create_kwargs["content_object"]) mock_cache.get.assert_called_once_with(cache_key) diff --git a/tests/test_model.py b/tests/test_model.py index 79e7651..618e0bc 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,10 +1,12 @@ import pytest from django_fsm import TransitionNotAllowed -from django_fsm_log.models import StateLog +from django_fsm_log.backends import _get_concrete_model from .models import Article, ArticleInteger +ConcreteModel = _get_concrete_model() + def test_get_available_state_transitions(article): assert len(list(article.get_available_state_transitions())) == 5 @@ -15,35 +17,35 @@ def test_get_all_state_transitions(article): def test_log_created_on_transition(article): - assert len(StateLog.objects.all()) == 0 + assert len(ConcreteModel.objects.all()) == 0 article.submit() article.save() - assert len(StateLog.objects.all()) == 1 + assert len(ConcreteModel.objects.all()) == 1 def test_log_not_created_if_transition_fails(article): - assert len(StateLog.objects.all()) == 0 + assert len(ConcreteModel.objects.all()) == 0 with pytest.raises(TransitionNotAllowed): article.publish() - assert len(StateLog.objects.all()) == 0 + assert len(ConcreteModel.objects.all()) == 0 def test_log_not_created_if_target_is_none(article): - assert len(StateLog.objects.all()) == 0 + assert len(ConcreteModel.objects.all()) == 0 article.validate_draft() - assert len(StateLog.objects.all()) == 0 + assert len(ConcreteModel.objects.all()) == 0 def test_by_is_set_when_passed_into_transition(article, user): article.submit(by=user) - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert user == log.by with pytest.raises(AttributeError): article.__django_fsm_log_attr_by # noqa: B018 @@ -52,7 +54,7 @@ def test_by_is_set_when_passed_into_transition(article, user): def test_by_is_none_when_not_set_in_transition(article): article.submit() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.by is None @@ -60,7 +62,7 @@ def test_description_is_set_when_passed_into_transition(article): description = "Lorem ipsum" article.submit(description=description) - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert description == log.description with pytest.raises(AttributeError): article.__django_fsm_log_attr_description # noqa: B018 @@ -69,7 +71,7 @@ def test_description_is_set_when_passed_into_transition(article): def test_description_is_none_when_not_set_in_transition(article): article.submit() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.description is None @@ -77,7 +79,7 @@ def test_description_can_be_mutated_by_the_transition(article): description = "Sed egestas dui" article.submit_inline_description_change(change_to=description) - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert description == log.description with pytest.raises(AttributeError): article.__django_fsm_log_attr_description # noqa: B018 @@ -89,7 +91,7 @@ def test_default_description(article): article.restore() article.save() - log = StateLog.objects.all()[1] + log = ConcreteModel.objects.all()[1] assert log.description == "Article restored" @@ -99,7 +101,7 @@ def test_default_description_call_priority(article): article.restore(description="Restored because of mistake") article.save() - log = StateLog.objects.all()[1] + log = ConcreteModel.objects.all()[1] assert log.description == "Restored because of mistake" @@ -107,28 +109,28 @@ def test_default_description_inline_priority(article): article.publish_as_temporary() article.save() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.description == "Article published (temporary)" def test_logged_state_is_new_state(article): article.submit() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.state == "submitted" def test_logged_transition_is_name_of_transition_method(article): article.submit() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.transition == "submit" def test_logged_content_object_is_instance_being_transitioned(article): article.submit() - log = StateLog.objects.all()[0] + log = ConcreteModel.objects.all()[0] assert log.content_object == article @@ -136,7 +138,7 @@ def test_get_display_state(article): article.submit() article.save() - log = StateLog.objects.latest() + log = ConcreteModel.objects.latest() article = Article.objects.get(pk=article.pk) prev_state = article.get_state_display() @@ -145,7 +147,7 @@ def test_get_display_state(article): article.publish() article.save() - log = StateLog.objects.latest() + log = ConcreteModel.objects.latest() article = Article.objects.get(pk=article.pk) @@ -157,7 +159,7 @@ def test_get_display_state_with_integer(article_integer): article_integer.change_to_two() article_integer.save() - log = StateLog.objects.latest() + log = ConcreteModel.objects.latest() article_integer = ArticleInteger.objects.get(pk=article_integer.pk) assert log.get_state_display() == article_integer.get_state_display() diff --git a/tests/test_settings.py b/tests/test_settings.py index 1373fcb..b275433 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,25 +1,25 @@ import pytest from django.conf import settings -from django_fsm_log.models import StateLog +from tests.models import PersistedTransition pytestmark = pytest.mark.ignore_article def test_log_not_created_if_model_ignored(article): - assert len(StateLog.objects.all()) == 0 + assert len(PersistedTransition.objects.all()) == 0 article.submit() article.save() - assert len(StateLog.objects.all()) == 0 + assert len(PersistedTransition.objects.all()) == 0 def test_log_created_on_transition_when_model_not_ignored(article): settings.DJANGO_FSM_LOG_IGNORED_MODELS = ["tests.models.SomeOtherModel"] - assert len(StateLog.objects.all()) == 0 + assert len(PersistedTransition.objects.all()) == 0 article.submit() article.save() - assert len(StateLog.objects.all()) == 1 + assert len(PersistedTransition.objects.all()) == 1 From 0819cd2c9658ede630cf868066d50db80663526b Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:39:14 +0200 Subject: [PATCH 02/10] Update README.md Co-authored-by: Daniel Hahler --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f552a74..0c0057e 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ by enabling a cached backend. See [Advanced Usage](#advanced-usage) - remove support for django 2.2 & 4.0 - Bring your own PersistedTransition model: - From this relase, django-fsm-log is deprecating StateLog and instead encourages you - to define for your own application the concrete model that will persist the transition + From this release, django-fsm-log is deprecating StateLog and instead encourages you + to define the concrete model for your own application that will persist the transition. ## 3.1.0 (2023-03-23) From f49a842301f1f139d2cc2e9ff7f0fe1157bb282d Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:39:25 +0200 Subject: [PATCH 03/10] Update README.md Co-authored-by: Daniel Hahler --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c0057e..7db6889 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ class FSMModelAdmin(admin.ModelAdmin): inlines = [PersistedTransitionInline] ``` -### Migration to Abstract model PersistedTransitionMixin +### Migration to abstract PersistedTransitionMixin model Once you defined your own model, you'll have to create the relevant migration to create the table. From 88c3f071397051c1acdb8fc89b1174208ee16f31 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:44:19 +0200 Subject: [PATCH 04/10] Update django_fsm_log/admin.py Co-authored-by: Daniel Hahler --- django_fsm_log/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/admin.py b/django_fsm_log/admin.py index 9acd9e6..ed63554 100644 --- a/django_fsm_log/admin.py +++ b/django_fsm_log/admin.py @@ -36,7 +36,7 @@ def get_queryset(self, request): def StateLogInline(*args, **kwargs): warn( - "StateLogInLine has been deprecated by PersistedTransitionInline.", + "StateLogInline has been deprecated by PersistedTransitionInline.", DeprecationWarning, stacklevel=2, ) From cd9854d3bc5d3e8ed3345ed554b3896dd978ca4a Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:44:35 +0200 Subject: [PATCH 05/10] Update django_fsm_log/managers.py Co-authored-by: Daniel Hahler --- django_fsm_log/managers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/managers.py b/django_fsm_log/managers.py index c3c238c..f961fd4 100644 --- a/django_fsm_log/managers.py +++ b/django_fsm_log/managers.py @@ -16,7 +16,7 @@ def for_(self, obj): def StateLogQuerySet(*args, **kwargs): - warn("StateLogQuerySet has been renamed PersistedTransitionQuerySet", DeprecationWarning, stacklevel=2) + warn("StateLogQuerySet has been renamed to PersistedTransitionQuerySet", DeprecationWarning, stacklevel=2) return PersistedTransitionQuerySet(*args, **kwargs) From 8a6834314fe6450355caedf2008b444ed4d410bf Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:44:48 +0200 Subject: [PATCH 06/10] Update django_fsm_log/managers.py Co-authored-by: Daniel Hahler --- django_fsm_log/managers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/managers.py b/django_fsm_log/managers.py index f961fd4..9459835 100644 --- a/django_fsm_log/managers.py +++ b/django_fsm_log/managers.py @@ -34,7 +34,7 @@ def __getattr__(self, attr, *args): def StateLogManager(*args, **kwargs): - warn("StateLogManager has been renamed PersistedTransitionManager", DeprecationWarning, stacklevel=2) + warn("StateLogManager has been renamed to PersistedTransitionManager", DeprecationWarning, stacklevel=2) return PersistedTransitionManager(*args, **kwargs) From 89c1e25dce920afa6b2a04789c6cda0ce40185ce Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:45:00 +0200 Subject: [PATCH 07/10] Update django_fsm_log/managers.py Co-authored-by: Daniel Hahler --- django_fsm_log/managers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/managers.py b/django_fsm_log/managers.py index 9459835..c0cee3d 100644 --- a/django_fsm_log/managers.py +++ b/django_fsm_log/managers.py @@ -61,5 +61,5 @@ def get_for_object(self, obj): def PendingStateLogManager(*args, **kwargs): - warn("PendingStateLogManager has been renamed PendingPersistedTransitionManager", DeprecationWarning, stacklevel=2) + warn("PendingStateLogManager has been renamed to PendingPersistedTransitionManager", DeprecationWarning, stacklevel=2) return PendingPersistedTransitionManager(*args, **kwargs) From 97675b896ed9c8778d19f33f48ccf6429441b696 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 10:45:06 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- django_fsm_log/managers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django_fsm_log/managers.py b/django_fsm_log/managers.py index c0cee3d..ec07d40 100644 --- a/django_fsm_log/managers.py +++ b/django_fsm_log/managers.py @@ -61,5 +61,9 @@ def get_for_object(self, obj): def PendingStateLogManager(*args, **kwargs): - warn("PendingStateLogManager has been renamed to PendingPersistedTransitionManager", DeprecationWarning, stacklevel=2) + warn( + "PendingStateLogManager has been renamed to PendingPersistedTransitionManager", + DeprecationWarning, + stacklevel=2, + ) return PendingPersistedTransitionManager(*args, **kwargs) From 86aaa11bc3191fbf7467c4618f87903feb6a3891 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:45:29 +0200 Subject: [PATCH 09/10] Update django_fsm_log/models.py Co-authored-by: Daniel Hahler --- django_fsm_log/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/models.py b/django_fsm_log/models.py index b5fe08d..8db7848 100644 --- a/django_fsm_log/models.py +++ b/django_fsm_log/models.py @@ -12,7 +12,7 @@ class PersistedTransitionMixin(models.Model): """ - Abstract Class that should be subclassed by the host application. + Abstract class that should be subclassed by the host application. Host projects should own the migrations and can decide on their own primary key type. """ From 92621806cd7133b177610a990b6d65139e163e50 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 5 Apr 2023 12:45:40 +0200 Subject: [PATCH 10/10] Update django_fsm_log/models.py Co-authored-by: Daniel Hahler --- django_fsm_log/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_fsm_log/models.py b/django_fsm_log/models.py index 8db7848..57ef951 100644 --- a/django_fsm_log/models.py +++ b/django_fsm_log/models.py @@ -61,7 +61,7 @@ def get_source_state_display(self): class StateLog(PersistedTransitionMixin): def __init__(self, *args, **kwargs): warn( - "StateLog model has been deprecated, you should now bring your own Model." + "StateLog model has been deprecated, you should now bring your own model." "\nPlease check the documentation at https://django-fsm-log.readthedocs.io/en/latest/" "\nto know how to.", DeprecationWarning,