11"""
22State tracking functionality for django models
33"""
4+ from __future__ import annotations
5+
46import inspect
5- from functools import partialmethod , wraps
7+ from functools import partialmethod
8+ from functools import wraps
69
7- import django
810from django .apps import apps as django_apps
911from django .db import models
1012from django .db .models import Field
1113from django .db .models .query_utils import DeferredAttribute
1214from django .db .models .signals import class_prepared
1315
14- from django_fsm .signals import pre_transition , post_transition
15-
16+ from django_fsm .signals import post_transition
17+ from django_fsm . signals import pre_transition
1618
1719__all__ = [
1820 "TransitionNotAllowed" ,
@@ -251,26 +253,9 @@ def deconstruct(self):
251253 return name , path , args , kwargs
252254
253255 def get_state (self , instance ):
254- # The state field may be deferred. We delegate the logic of figuring
255- # this out and loading the deferred field on-demand to Django's
256- # built-in DeferredAttribute class. DeferredAttribute's instantiation
257- # signature changed over time, so we need to check Django version
258- # before proceeding to call DeferredAttribute. An alternative to this
259- # would be copying the latest implementation of DeferredAttribute to
260- # django_fsm, but this comes with the added responsibility of keeping
261- # the copied code up to date.
262- if django .VERSION [:3 ] >= (3 , 0 , 0 ):
263- return DeferredAttribute (self ).__get__ (instance )
264- elif django .VERSION [:3 ] >= (2 , 1 , 0 ):
265- return DeferredAttribute (self .name ).__get__ (instance )
266- elif django .VERSION [:3 ] >= (1 , 10 , 0 ):
267- return DeferredAttribute (self .name , model = None ).__get__ (instance )
268- else :
269- # The field was either not deferred (in which case we can return it
270- # right away) or ir was, but we are running on an unknown version
271- # of Django and we do not know the appropriate DeferredAttribute
272- # interface, and accessing the field will raise KeyError.
273- return instance .__dict__ [self .name ]
256+ # The state field may be deferred. We delegate the logic of figuring this out
257+ # and loading the deferred field on-demand to Django's built-in DeferredAttribute class.
258+ return DeferredAttribute (self ).__get__ (instance )
274259
275260 def set_state (self , instance , state ):
276261 instance .__dict__ [self .name ] = state
@@ -435,14 +420,15 @@ def set_state(self, instance, state):
435420 instance .__dict__ [self .attname ] = self .to_python (state )
436421
437422
438- class FSMModelMixin ( object ) :
423+ class FSMModelMixin :
439424 """
440425 Mixin that allows refresh_from_db for models with fsm protected fields
441426 """
442427
443428 def _get_protected_fsm_fields (self ):
444429 def is_fsm_and_protected (f ):
445430 return isinstance (f , FSMFieldMixin ) and f .protected
431+
446432 protected_fields = filter (is_fsm_and_protected , self ._meta .concrete_fields )
447433 return {f .attname for f in protected_fields }
448434
@@ -455,13 +441,13 @@ def refresh_from_db(self, *args, **kwargs):
455441 protected_fields = self ._get_protected_fsm_fields ()
456442 skipped_fields = deferred_fields .union (protected_fields )
457443
458- fields = [f .attname for f in self ._meta .concrete_fields
459- if f .attname not in skipped_fields ]
444+ fields = [f .attname for f in self ._meta .concrete_fields if f .attname not in skipped_fields ]
445+
446+ kwargs ["fields" ] = fields
447+ super ().refresh_from_db (* args , ** kwargs )
460448
461- kwargs ['fields' ] = fields
462- super (FSMModelMixin , self ).refresh_from_db (* args , ** kwargs )
463449
464- class ConcurrentTransitionMixin ( object ) :
450+ class ConcurrentTransitionMixin :
465451 """
466452 Protects a Model from undesirable effects caused by concurrently executed transitions,
467453 e.g. running the same transition multiple times at the same time, or running different
0 commit comments