|
@@ -1,7 +1,8 @@
|
|
|
from __future__ import annotations
|
|
|
|
|
|
-from typing import Any, Callable, Iterable, Mapping, Tuple, cast
|
|
|
+from typing import Any, Callable, Iterable, Mapping, Tuple, Type, cast
|
|
|
|
|
|
+from django.apps.config import AppConfig
|
|
|
from django.db import models
|
|
|
from django.db.models import signals
|
|
|
from django.utils import timezone
|
|
@@ -17,6 +18,7 @@ __all__ = (
|
|
|
"Model",
|
|
|
"DefaultFieldsModel",
|
|
|
"sane_repr",
|
|
|
+ "get_model_if_available",
|
|
|
"control_silo_model",
|
|
|
"region_silo_model",
|
|
|
)
|
|
@@ -142,6 +144,23 @@ signals.post_save.connect(__model_post_save)
|
|
|
signals.class_prepared.connect(__model_class_prepared)
|
|
|
|
|
|
|
|
|
+def get_model_if_available(app_config: AppConfig, model_name: str) -> Type[models.Model] | None:
|
|
|
+ """Get a named model class if it exists and is available in this silo mode."""
|
|
|
+ try:
|
|
|
+ model = app_config.get_model(model_name)
|
|
|
+ except LookupError:
|
|
|
+ return None
|
|
|
+ assert isinstance(model, type) and issubclass(model, models.Model)
|
|
|
+
|
|
|
+ silo_limit = getattr(model._meta, "silo_limit", None) # type: ignore
|
|
|
+ if silo_limit is not None:
|
|
|
+ assert isinstance(silo_limit, ModelSiloLimit)
|
|
|
+ if not silo_limit.is_available():
|
|
|
+ return None
|
|
|
+
|
|
|
+ return model
|
|
|
+
|
|
|
+
|
|
|
class ModelSiloLimit(SiloLimit):
|
|
|
def __init__(
|
|
|
self,
|
|
@@ -199,9 +218,7 @@ class ModelSiloLimit(SiloLimit):
|
|
|
# trigger hooks in Django's ModelBase metaclass a second time.
|
|
|
setattr(model_class, model_attr_name, override)
|
|
|
|
|
|
- # For internal tooling only. Having any production logic depend on this is
|
|
|
- # strongly discouraged.
|
|
|
- model_class._meta.__silo_limit = self
|
|
|
+ model_class._meta.silo_limit = self
|
|
|
|
|
|
return model_class
|
|
|
|