Plain is headed towards 1.0! Subscribe for development updates →

 1from __future__ import annotations
 2
 3import functools
 4from collections import namedtuple
 5from collections.abc import Generator
 6from typing import Any
 7
 8
 9def make_model_tuple(model: Any) -> tuple[str, str]:
10    """
11    Take a model or a string of the form "package_label.ModelName" and return a
12    corresponding ("package_label", "modelname") tuple. If a tuple is passed in,
13    assume it's a valid model tuple already and return it unchanged.
14    """
15    try:
16        if isinstance(model, tuple):
17            model_tuple = model
18        elif isinstance(model, str):
19            package_label, model_name = model.split(".")
20            model_tuple = package_label, model_name.lower()
21        else:
22            model_tuple = (
23                model.model_options.package_label,
24                model.model_options.model_name,
25            )
26        assert len(model_tuple) == 2
27        return model_tuple
28    except (ValueError, AssertionError):
29        raise ValueError(
30            f"Invalid model reference '{model}'. String model references "
31            "must be of the form 'package_label.ModelName'."
32        )
33
34
35def resolve_callables(
36    mapping: dict[str, Any],
37) -> Generator[tuple[str, Any], None, None]:
38    """
39    Generate key/value pairs for the given mapping where the values are
40    evaluated if they're callable.
41    """
42    for k, v in mapping.items():
43        yield k, v() if callable(v) else v
44
45
46def unpickle_named_row(
47    names: tuple[str, ...], values: tuple[Any, ...]
48) -> tuple[Any, ...]:
49    return create_namedtuple_class(*names)(*values)
50
51
52@functools.lru_cache
53def create_namedtuple_class(*names: str) -> type[tuple[Any, ...]]:
54    # Cache type() with @lru_cache since it's too slow to be called for every
55    # QuerySet evaluation.
56    def __reduce__(self: Any) -> tuple[Any, tuple[tuple[str, ...], tuple[Any, ...]]]:
57        return unpickle_named_row, (names, tuple(self))
58
59    return type(
60        "Row",
61        (namedtuple("Row", names),),
62        {"__reduce__": __reduce__, "__slots__": ()},
63    )