mixins.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import warnings
  2. from django.core import checks
  3. from django.utils.deprecation import RemovedInDjango60Warning
  4. from django.utils.functional import cached_property
  5. NOT_PROVIDED = object()
  6. class FieldCacheMixin:
  7. """
  8. An API for working with the model's fields value cache.
  9. Subclasses must set self.cache_name to a unique entry for the cache -
  10. typically the field’s name.
  11. """
  12. # RemovedInDjango60Warning.
  13. def get_cache_name(self):
  14. raise NotImplementedError
  15. @cached_property
  16. def cache_name(self):
  17. # RemovedInDjango60Warning: when the deprecation ends, replace with:
  18. # raise NotImplementedError
  19. cache_name = self.get_cache_name()
  20. warnings.warn(
  21. f"Override {self.__class__.__qualname__}.cache_name instead of "
  22. "get_cache_name().",
  23. RemovedInDjango60Warning,
  24. stacklevel=3,
  25. )
  26. return cache_name
  27. def get_cached_value(self, instance, default=NOT_PROVIDED):
  28. try:
  29. return instance._state.fields_cache[self.cache_name]
  30. except KeyError:
  31. if default is NOT_PROVIDED:
  32. raise
  33. return default
  34. def is_cached(self, instance):
  35. return self.cache_name in instance._state.fields_cache
  36. def set_cached_value(self, instance, value):
  37. instance._state.fields_cache[self.cache_name] = value
  38. def delete_cached_value(self, instance):
  39. del instance._state.fields_cache[self.cache_name]
  40. class CheckFieldDefaultMixin:
  41. _default_hint = ("<valid default>", "<invalid default>")
  42. def _check_default(self):
  43. if (
  44. self.has_default()
  45. and self.default is not None
  46. and not callable(self.default)
  47. ):
  48. return [
  49. checks.Warning(
  50. "%s default should be a callable instead of an instance "
  51. "so that it's not shared between all field instances."
  52. % (self.__class__.__name__,),
  53. hint=(
  54. "Use a callable instead, e.g., use `%s` instead of "
  55. "`%s`." % self._default_hint
  56. ),
  57. obj=self,
  58. id="fields.E010",
  59. )
  60. ]
  61. else:
  62. return []
  63. def check(self, **kwargs):
  64. errors = super().check(**kwargs)
  65. errors.extend(self._check_default())
  66. return errors