Coverage for jutil/cache.py: 0%

41 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-07 16:40 -0500

1import logging 

2from typing import Optional, TYPE_CHECKING, Any, Sequence, List 

3 

4logger = logging.getLogger(__name__) 

5 

6 

7class CachedFieldsMixin: 

8 """ 

9 Cached fields mixin. Usage: 

10 1) List cached field names in cached_fields list 

11 2) Implement get_xxx functions where xxx is cached field name 

12 3) Call update_cached_fields() to refresh 

13 4) Optionally call update_cached_fields_pre_save() on pre_save signal for objects (to automatically refresh on save) 

14 """ 

15 

16 cached_fields: Sequence[str] = [] 

17 

18 if TYPE_CHECKING: 

19 pk: Any = None 

20 

21 def save(self, force_insert=False, force_update=False, using=None, update_fields=None): 

22 pass 

23 

24 def update_cached_fields( 

25 self, commit: bool = True, exceptions: bool = True, updated_fields: Optional[Sequence[str]] = None, force: bool = False 

26 ) -> List[str]: 

27 """ 

28 Updates cached fields using get_xxx calls for each cached field (in cached_fields list). 

29 :param commit: Save update fields to DB 

30 :param exceptions: Raise exceptions or not 

31 :param updated_fields: List of cached fields to update. Pass None for all cached fields. 

32 :param force: Force commit of all cached fields even if nothing changed 

33 :return: List of changed fields 

34 """ 

35 changed_fields: List[str] = [] 

36 try: 

37 fields = updated_fields or self.cached_fields 

38 for k in fields: 

39 f = "get_" + k 

40 if not hasattr(self, f): 

41 raise Exception("Field {k} marked as cached in {obj} but function get_{k}() does not exist".format(k=k, obj=self)) 

42 v = self.__getattribute__(f)() 

43 if force or getattr(self, k) != v: 

44 setattr(self, k, v) 

45 changed_fields.append(k) 

46 if commit and changed_fields: 

47 self.save(update_fields=changed_fields) # pytype: disable=attribute-error 

48 return changed_fields 

49 except Exception as err: 

50 logger.warning("%s update_cached_fields failed for %s: %s", self.__class__.__name__, self, err) 

51 if exceptions: 

52 raise err 

53 return changed_fields 

54 

55 def update_cached_fields_pre_save(self, update_fields: Optional[Sequence[str]]) -> List[str]: 

56 """ 

57 Call on pre_save signal for objects (to automatically refresh on save). 

58 :param update_fields: list of fields to update 

59 :return: List of changed fields 

60 """ 

61 if hasattr(self, "pk") and self.pk and update_fields is None: 

62 return self.update_cached_fields(commit=False, exceptions=False) 

63 return [] 

64 

65 

66def update_cached_fields(*args): 

67 """ 

68 Calls update_cached_fields() for each object passed in as argument. 

69 Supports also iterable objects by checking __iter__ attribute. 

70 :param args: List of objects 

71 :return: None 

72 """ 

73 for a in args: 

74 if a is not None: 

75 if hasattr(a, "__iter__"): 

76 for e in a: 

77 if e is not None: 

78 e.update_cached_fields() 

79 else: 

80 a.update_cached_fields()