Django 重命名模型。创建新 django_content_type 而不是重命名旧记录
Django rename model. New django_content_type is created instead of renaming the old record
设置:
Django 1.7 | Postgres 9.x
class Buildings(BaseModel):
number = models.CharField(max_length=25)
class TestGeneric(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
假设我创建了一个 TestGeneric
实例,将其与 Building
关联并保存:
TestGeneric.objects.create(content_object=Building.objects.first())
现在我将 Buildings
重命名为 Building
并运行 makemigrations
。我被提示 Did you rename the app.Buildings model to Building? [y/N]
我选择是。然后我运行 migrate
并得到:
The following content types are stale and need to be deleted:
app | buildings
Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
无论我回答什么,Django 都会自动在 django_content_type
中创建一个新行,并使用 building
作为名称和标签。有没有什么方法可以重命名 ContentType
,这样我所有的 TestGeneric 行都不会被吹走?
我刚刚在一个项目中使用了它;需要注意的是,如果您在尝试应用自动创建的模型重命名迁移之前创建迁移,则此操作没有问题。
您需要更改应用名称、型号名称和之前的迁移以匹配您的设置;在此示例中,我们将模型的名称从 profile
更改为 member
.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from django.conf import settings
sql = """UPDATE django_content_type
SET name = 'member',
model = 'member'
WHERE name = 'profile' AND
model = 'profile' AND
app_label = 'open_humans';"""
reverse_sql = """UPDATE django_content_type
SET name = 'profile',
model = 'profile'
WHERE name = 'member' AND
model = 'member' AND
app_label = 'open_humans';"""
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('open_humans', '0004_auto_20150106_1828'),
]
operations = [
migrations.RunSQL(sql, reverse_sql)
]
我可以分享一下针对这个问题写的迁移操作:
from django.db import migrations
from django.contrib.contenttypes.models import ContentType
class UpdateContentType(migrations.RunPython):
'''Database migration operation to update a ContentType'''
def _update_contenttype_func(self, old_app: str, old_model: str, new_app: str, new_model: str):
def func(apps, schema_editor):
ContentType.objects \
.filter(app_label=old_app, model=old_model) \
.update(app_label=new_app, model=new_model)
ContentType.objects.clear_cache()
return func
def __init__(self, app: str, model: str, new_app: str = None, new_model: str = None):
if new_app is None:
new_app = app
if new_model is None:
new_model = model
self.app = app
self.model = model
self.new_app = new_app
self.new_model = new_model
super().__init__(
code=self._update_contenttype_func(
old_app=app, old_model=model, new_app=new_app, new_model=new_model
),
reverse_code=self._update_contenttype_func(
old_app=new_app, old_model=new_model, new_app=app, new_model=model
),
)
def describe(self):
return (f"Update ContentType {self.app}.{self.model}"
f" to {self.new_app}.{self.new_model}")
每当重命名模型时,我都会编辑迁移文件并添加一个 UpdateContentType
操作:
from django.db import migrations
from apps.utils.migrations_util import UpdateContentType
class Migration(migrations.Migration):
dependencies = [
('myapp', '0010_previous_migration'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.RenameModel(old_name='OldModel', new_name='NewModel'),
UpdateContentType(app='myapp', model='oldmodel', new_model='newmodel'),
]
设置:
Django 1.7 | Postgres 9.x
class Buildings(BaseModel):
number = models.CharField(max_length=25)
class TestGeneric(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
假设我创建了一个 TestGeneric
实例,将其与 Building
关联并保存:
TestGeneric.objects.create(content_object=Building.objects.first())
现在我将 Buildings
重命名为 Building
并运行 makemigrations
。我被提示 Did you rename the app.Buildings model to Building? [y/N]
我选择是。然后我运行 migrate
并得到:
The following content types are stale and need to be deleted:
app | buildings
Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
无论我回答什么,Django 都会自动在 django_content_type
中创建一个新行,并使用 building
作为名称和标签。有没有什么方法可以重命名 ContentType
,这样我所有的 TestGeneric 行都不会被吹走?
我刚刚在一个项目中使用了它;需要注意的是,如果您在尝试应用自动创建的模型重命名迁移之前创建迁移,则此操作没有问题。
您需要更改应用名称、型号名称和之前的迁移以匹配您的设置;在此示例中,我们将模型的名称从 profile
更改为 member
.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from django.conf import settings
sql = """UPDATE django_content_type
SET name = 'member',
model = 'member'
WHERE name = 'profile' AND
model = 'profile' AND
app_label = 'open_humans';"""
reverse_sql = """UPDATE django_content_type
SET name = 'profile',
model = 'profile'
WHERE name = 'member' AND
model = 'member' AND
app_label = 'open_humans';"""
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('open_humans', '0004_auto_20150106_1828'),
]
operations = [
migrations.RunSQL(sql, reverse_sql)
]
我可以分享一下针对这个问题写的迁移操作:
from django.db import migrations
from django.contrib.contenttypes.models import ContentType
class UpdateContentType(migrations.RunPython):
'''Database migration operation to update a ContentType'''
def _update_contenttype_func(self, old_app: str, old_model: str, new_app: str, new_model: str):
def func(apps, schema_editor):
ContentType.objects \
.filter(app_label=old_app, model=old_model) \
.update(app_label=new_app, model=new_model)
ContentType.objects.clear_cache()
return func
def __init__(self, app: str, model: str, new_app: str = None, new_model: str = None):
if new_app is None:
new_app = app
if new_model is None:
new_model = model
self.app = app
self.model = model
self.new_app = new_app
self.new_model = new_model
super().__init__(
code=self._update_contenttype_func(
old_app=app, old_model=model, new_app=new_app, new_model=new_model
),
reverse_code=self._update_contenttype_func(
old_app=new_app, old_model=new_model, new_app=app, new_model=model
),
)
def describe(self):
return (f"Update ContentType {self.app}.{self.model}"
f" to {self.new_app}.{self.new_model}")
每当重命名模型时,我都会编辑迁移文件并添加一个 UpdateContentType
操作:
from django.db import migrations
from apps.utils.migrations_util import UpdateContentType
class Migration(migrations.Migration):
dependencies = [
('myapp', '0010_previous_migration'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.RenameModel(old_name='OldModel', new_name='NewModel'),
UpdateContentType(app='myapp', model='oldmodel', new_model='newmodel'),
]