123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- from django.apps import apps as global_apps
- from django.db import DEFAULT_DB_ALIAS, migrations, router, transaction
- from django.db.utils import IntegrityError
-
-
- class RenameContentType(migrations.RunPython):
- def __init__(self, app_label, old_model, new_model):
- self.app_label = app_label
- self.old_model = old_model
- self.new_model = new_model
- super().__init__(self.rename_forward, self.rename_backward)
-
- def _rename(self, apps, schema_editor, old_model, new_model):
- ContentType = apps.get_model('contenttypes', 'ContentType')
- db = schema_editor.connection.alias
- if not router.allow_migrate_model(db, ContentType):
- return
-
- try:
- content_type = ContentType.objects.db_manager(db).get_by_natural_key(self.app_label, old_model)
- except ContentType.DoesNotExist:
- pass
- else:
- content_type.model = new_model
- try:
- with transaction.atomic(using=db):
- content_type.save(update_fields={'model'})
- except IntegrityError:
- # Gracefully fallback if a stale content type causes a
- # conflict as remove_stale_contenttypes will take care of
- # asking the user what should be done next.
- content_type.model = old_model
- else:
- # Clear the cache as the `get_by_natual_key()` call will cache
- # the renamed ContentType instance by its old model name.
- ContentType.objects.clear_cache()
-
- def rename_forward(self, apps, schema_editor):
- self._rename(apps, schema_editor, self.old_model, self.new_model)
-
- def rename_backward(self, apps, schema_editor):
- self._rename(apps, schema_editor, self.new_model, self.old_model)
-
-
- def inject_rename_contenttypes_operations(plan=None, apps=global_apps, using=DEFAULT_DB_ALIAS, **kwargs):
- """
- Insert a `RenameContentType` operation after every planned `RenameModel`
- operation.
- """
- if plan is None:
- return
-
- # Determine whether or not the ContentType model is available.
- try:
- ContentType = apps.get_model('contenttypes', 'ContentType')
- except LookupError:
- available = False
- else:
- if not router.allow_migrate_model(using, ContentType):
- return
- available = True
-
- for migration, backward in plan:
- if (migration.app_label, migration.name) == ('contenttypes', '0001_initial'):
- # There's no point in going forward if the initial contenttypes
- # migration is unapplied as the ContentType model will be
- # unavailable from this point.
- if backward:
- break
- else:
- available = True
- continue
- # The ContentType model is not available yet.
- if not available:
- continue
- inserts = []
- for index, operation in enumerate(migration.operations):
- if isinstance(operation, migrations.RenameModel):
- operation = RenameContentType(
- migration.app_label, operation.old_name_lower, operation.new_name_lower
- )
- inserts.append((index + 1, operation))
- for inserted, (index, operation) in enumerate(inserts):
- migration.operations.insert(inserted + index, operation)
-
-
- def get_contenttypes_and_models(app_config, using, ContentType):
- if not router.allow_migrate_model(using, ContentType):
- return None, None
-
- ContentType.objects.clear_cache()
-
- content_types = {
- ct.model: ct
- for ct in ContentType.objects.using(using).filter(app_label=app_config.label)
- }
- app_models = {
- model._meta.model_name: model
- for model in app_config.get_models()
- }
- return content_types, app_models
-
-
- def create_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
- """
- Create content types for models in the given app.
- """
- if not app_config.models_module:
- return
-
- app_label = app_config.label
- try:
- app_config = apps.get_app_config(app_label)
- ContentType = apps.get_model('contenttypes', 'ContentType')
- except LookupError:
- return
-
- content_types, app_models = get_contenttypes_and_models(app_config, using, ContentType)
-
- if not app_models:
- return
-
- cts = [
- ContentType(
- app_label=app_label,
- model=model_name,
- )
- for (model_name, model) in app_models.items()
- if model_name not in content_types
- ]
- ContentType.objects.using(using).bulk_create(cts)
- if verbosity >= 2:
- for ct in cts:
- print("Adding content type '%s | %s'" % (ct.app_label, ct.model))
|