patched_project_state.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. from contextlib import contextmanager
  2. from unittest import mock
  3. from django.db.migrations.state import ProjectState
  4. from psqlextra.models import (
  5. PostgresMaterializedViewModel,
  6. PostgresPartitionedModel,
  7. PostgresViewModel,
  8. )
  9. from .state import (
  10. PostgresMaterializedViewModelState,
  11. PostgresPartitionedModelState,
  12. PostgresViewModelState,
  13. )
  14. # original `ProjectState.from_apps` function,
  15. # saved here so the patched version can call
  16. # the original
  17. original_from_apps = ProjectState.from_apps
  18. def project_state_from_apps(apps):
  19. """Creates a :see:ProjectState instance from the specified list of apps."""
  20. project_state = original_from_apps(apps)
  21. for model in apps.get_models(include_swapped=True):
  22. model_state = None
  23. # for some of our custom models, use the more specific model
  24. # state.. for everything else, business as usual
  25. if issubclass(model, PostgresPartitionedModel):
  26. model_state = PostgresPartitionedModelState.from_model(model)
  27. elif issubclass(model, PostgresMaterializedViewModel):
  28. model_state = PostgresMaterializedViewModelState.from_model(model)
  29. elif issubclass(model, PostgresViewModel):
  30. model_state = PostgresViewModelState.from_model(model)
  31. else:
  32. continue
  33. model_state_key = (model_state.app_label, model_state.name_lower)
  34. project_state.models[model_state_key] = model_state
  35. return project_state
  36. @contextmanager
  37. def patched_project_state():
  38. """Patches the standard Django :see:ProjectState.from_apps for the duration
  39. of the context.
  40. The patch intercepts the `from_apps` function to control
  41. how model state is creatd. We want to use our custom
  42. model state classes for certain types of models.
  43. We have to do this because there is no way in Django
  44. to extend the project state otherwise.
  45. """
  46. from_apps_module_path = "django.db.migrations.state"
  47. from_apps_class_path = f"{from_apps_module_path}.ProjectState"
  48. from_apps_path = f"{from_apps_class_path}.from_apps"
  49. with mock.patch(from_apps_path, new=project_state_from_apps):
  50. yield