utils.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import functools
  2. from collections import namedtuple
  3. def make_model_tuple(model):
  4. """
  5. Take a model or a string of the form "app_label.ModelName" and return a
  6. corresponding ("app_label", "modelname") tuple. If a tuple is passed in,
  7. assume it's a valid model tuple already and return it unchanged.
  8. """
  9. try:
  10. if isinstance(model, tuple):
  11. model_tuple = model
  12. elif isinstance(model, str):
  13. app_label, model_name = model.split(".")
  14. model_tuple = app_label, model_name.lower()
  15. else:
  16. model_tuple = model._meta.app_label, model._meta.model_name
  17. assert len(model_tuple) == 2
  18. return model_tuple
  19. except (ValueError, AssertionError):
  20. raise ValueError(
  21. "Invalid model reference '%s'. String model references "
  22. "must be of the form 'app_label.ModelName'." % model
  23. )
  24. def resolve_callables(mapping):
  25. """
  26. Generate key/value pairs for the given mapping where the values are
  27. evaluated if they're callable.
  28. """
  29. for k, v in mapping.items():
  30. yield k, v() if callable(v) else v
  31. def unpickle_named_row(names, values):
  32. return create_namedtuple_class(*names)(*values)
  33. @functools.lru_cache
  34. def create_namedtuple_class(*names):
  35. # Cache type() with @lru_cache since it's too slow to be called for every
  36. # QuerySet evaluation.
  37. def __reduce__(self):
  38. return unpickle_named_row, (names, tuple(self))
  39. return type(
  40. "Row",
  41. (namedtuple("Row", names),),
  42. {"__reduce__": __reduce__, "__slots__": ()},
  43. )
  44. class AltersData:
  45. """
  46. Make subclasses preserve the alters_data attribute on overridden methods.
  47. """
  48. def __init_subclass__(cls, **kwargs):
  49. for fn_name, fn in vars(cls).items():
  50. if callable(fn) and not hasattr(fn, "alters_data"):
  51. for base in cls.__bases__:
  52. if base_fn := getattr(base, fn_name, None):
  53. if hasattr(base_fn, "alters_data"):
  54. fn.alters_data = base_fn.alters_data
  55. break
  56. super().__init_subclass__(**kwargs)