Coverage for jutil/cache.py: 0%
41 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-07 16:40 -0500
« 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
4logger = logging.getLogger(__name__)
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 """
16 cached_fields: Sequence[str] = []
18 if TYPE_CHECKING:
19 pk: Any = None
21 def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
22 pass
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
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 []
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()