You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from django.apps import apps as global_apps
  2. from django.db import DEFAULT_DB_ALIAS, migrations, router, transaction
  3. from django.db.utils import IntegrityError
  4. class RenameContentType(migrations.RunPython):
  5. def __init__(self, app_label, old_model, new_model):
  6. self.app_label = app_label
  7. self.old_model = old_model
  8. self.new_model = new_model
  9. super().__init__(self.rename_forward, self.rename_backward)
  10. def _rename(self, apps, schema_editor, old_model, new_model):
  11. ContentType = apps.get_model('contenttypes', 'ContentType')
  12. db = schema_editor.connection.alias
  13. if not router.allow_migrate_model(db, ContentType):
  14. return
  15. try:
  16. content_type = ContentType.objects.db_manager(db).get_by_natural_key(self.app_label, old_model)
  17. except ContentType.DoesNotExist:
  18. pass
  19. else:
  20. content_type.model = new_model
  21. try:
  22. with transaction.atomic(using=db):
  23. content_type.save(update_fields={'model'})
  24. except IntegrityError:
  25. # Gracefully fallback if a stale content type causes a
  26. # conflict as remove_stale_contenttypes will take care of
  27. # asking the user what should be done next.
  28. content_type.model = old_model
  29. else:
  30. # Clear the cache as the `get_by_natual_key()` call will cache
  31. # the renamed ContentType instance by its old model name.
  32. ContentType.objects.clear_cache()
  33. def rename_forward(self, apps, schema_editor):
  34. self._rename(apps, schema_editor, self.old_model, self.new_model)
  35. def rename_backward(self, apps, schema_editor):
  36. self._rename(apps, schema_editor, self.new_model, self.old_model)
  37. def inject_rename_contenttypes_operations(plan=None, apps=global_apps, using=DEFAULT_DB_ALIAS, **kwargs):
  38. """
  39. Insert a `RenameContentType` operation after every planned `RenameModel`
  40. operation.
  41. """
  42. if plan is None:
  43. return
  44. # Determine whether or not the ContentType model is available.
  45. try:
  46. ContentType = apps.get_model('contenttypes', 'ContentType')
  47. except LookupError:
  48. available = False
  49. else:
  50. if not router.allow_migrate_model(using, ContentType):
  51. return
  52. available = True
  53. for migration, backward in plan:
  54. if (migration.app_label, migration.name) == ('contenttypes', '0001_initial'):
  55. # There's no point in going forward if the initial contenttypes
  56. # migration is unapplied as the ContentType model will be
  57. # unavailable from this point.
  58. if backward:
  59. break
  60. else:
  61. available = True
  62. continue
  63. # The ContentType model is not available yet.
  64. if not available:
  65. continue
  66. inserts = []
  67. for index, operation in enumerate(migration.operations):
  68. if isinstance(operation, migrations.RenameModel):
  69. operation = RenameContentType(
  70. migration.app_label, operation.old_name_lower, operation.new_name_lower
  71. )
  72. inserts.append((index + 1, operation))
  73. for inserted, (index, operation) in enumerate(inserts):
  74. migration.operations.insert(inserted + index, operation)
  75. def get_contenttypes_and_models(app_config, using, ContentType):
  76. if not router.allow_migrate_model(using, ContentType):
  77. return None, None
  78. ContentType.objects.clear_cache()
  79. content_types = {
  80. ct.model: ct
  81. for ct in ContentType.objects.using(using).filter(app_label=app_config.label)
  82. }
  83. app_models = {
  84. model._meta.model_name: model
  85. for model in app_config.get_models()
  86. }
  87. return content_types, app_models
  88. def create_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
  89. """
  90. Create content types for models in the given app.
  91. """
  92. if not app_config.models_module:
  93. return
  94. app_label = app_config.label
  95. try:
  96. app_config = apps.get_app_config(app_label)
  97. ContentType = apps.get_model('contenttypes', 'ContentType')
  98. except LookupError:
  99. return
  100. content_types, app_models = get_contenttypes_and_models(app_config, using, ContentType)
  101. if not app_models:
  102. return
  103. cts = [
  104. ContentType(
  105. app_label=app_label,
  106. model=model_name,
  107. )
  108. for (model_name, model) in app_models.items()
  109. if model_name not in content_types
  110. ]
  111. ContentType.objects.using(using).bulk_create(cts)
  112. if verbosity >= 2:
  113. for ct in cts:
  114. print("Adding content type '%s | %s'" % (ct.app_label, ct.model))