Python 使用所需的ForeignKey引用在Django(1.8)应用程序之间移动模型
这是对这个问题的延伸: 我需要将一组模型从Python 使用所需的ForeignKey引用在Django(1.8)应用程序之间移动模型,python,database,django-models,schema-migration,Python,Database,Django Models,Schema Migration,这是对这个问题的延伸: 我需要将一组模型从旧应用程序移动到新应用程序。最好的答案似乎是,但由于需要外键引用,事情就有点棘手了@halfnibble在对Ozan答案的评论中提出了一个解决方案,但我仍然无法确定步骤的精确顺序(例如,我何时将模型复制到新应用程序,何时从旧应用程序中删除模型,哪些迁移将位于旧应用程序迁移与新应用程序迁移,等等) 非常感谢您的帮助 在应用程序之间迁移模型。 简而言之,不要这样做 但这个答案在现实生活中的项目和生产数据库中很少起作用。因此,我创建了一个示例来演示这个相当复杂
旧应用程序
移动到新应用程序
。最好的答案似乎是,但由于需要外键引用,事情就有点棘手了@halfnibble在对Ozan答案的评论中提出了一个解决方案,但我仍然无法确定步骤的精确顺序(例如,我何时将模型复制到新应用程序
,何时从旧应用程序
中删除模型,哪些迁移将位于旧应用程序迁移
与新应用程序迁移
,等等)
非常感谢您的帮助 在应用程序之间迁移模型。 简而言之,不要这样做 但这个答案在现实生活中的项目和生产数据库中很少起作用。因此,我创建了一个示例来演示这个相当复杂的过程 我正在使用MySQL。(不,那不是我真正的证件) 问题 我正在使用的示例是一个工厂项目,它有一个cars应用程序,最初有一个
Car
模型和一个轮胎
模型
factory
|_ cars
|_ Car
|_ Tires
汽车
型号与轮胎
有外键关系。(如中所示,通过汽车模型指定轮胎)
然而,我们很快意识到,轮胎
将是一个拥有自己视图等的大型模型,因此我们希望它出现在自己的应用程序中。因此,理想的结构是:
factory
|_ cars
|_ Car
|_ tires
|_ Tires
我们需要保持汽车
和轮胎
之间的外键关系,因为太多依赖于数据的保存
解决方案
第1步。设置设计不好的初始应用程序
浏览
步骤2。创建一个管理界面并添加一组包含外键关系的数据
看法
步骤3。决定将轮胎
模型移动到自己的应用程序中。精心剪切并将代码粘贴到新的轮胎应用程序中。确保更新汽车
型号,以指向新的轮胎。轮胎
型号
然后运行/manage.py makemigrations
并将数据库备份到某个地方(以防出现严重故障)
最后,运行/manage.py migrate
并查看doom的错误消息
django.db.utils.IntegrityError:(1217,“无法删除或更新父行:外键约束失败”)
在中查看到目前为止的代码和迁移
第4步。棘手的部分。自动生成的迁移无法看到您只是将模型复制到其他应用程序。因此,我们必须做一些事情来弥补这一点
您可以在“我做了这个测试”中查看最后的迁移,并附上注释,以验证它是否有效
首先,我们将研究汽车
。您必须进行新的空迁移。实际上,此迁移需要在最近创建的迁移(未能执行的迁移)之前运行。因此,我对我创建的迁移进行了重新编号,并更改了依赖项,以首先运行我的自定义迁移,然后运行cars
app的最后一次自动生成的迁移
您可以使用以下方法创建空迁移:
./manage.py makemigrations --empty cars
步骤4.a.进行自定义旧应用程序迁移
在第一次自定义迁移中,我只执行“数据库操作”迁移。Django为您提供了拆分“状态”和“数据库”操作的选项。通过查看,您可以看到这是如何完成的
我在这第一步中的目标是将数据库表从oldapp_model
重命名为newapp_model
,而不影响Django的状态。您必须根据应用程序名和模型名来确定Django将为您的数据库表命名什么
现在,您可以修改初始的tires
迁移了
步骤4.b.修改新应用程序初始迁移
操作很好,但我们只想修改“状态”,而不想修改数据库。为什么?因为我们保留了cars
应用程序中的数据库表。此外,您需要确保以前进行的自定义迁移是此迁移的依赖项。看看轮胎
因此,现在我们在数据库中将cars.Tires
重命名为Tires.Tires
,并更改了Django状态以识别Tires.Tires
表
步骤4.c.修改旧应用程序上次自动生成的迁移
回到汽车,我们需要修改上次自动生成的迁移。它应该需要我们的第一个定制汽车迁移,以及初始轮胎迁移(我们刚刚修改)
这里我们应该保留AlterField
操作,因为Car
模型指向不同的模型(即使它具有相同的数据)。但是,我们需要删除有关DeleteModel
的迁移行,因为cars.Tires
模型不再存在。它已完全转换为轮胎。轮胎
。视图
步骤4.d.清理旧应用程序中的过时模型
最后但并非最不重要的一点是,您需要在cars应用程序中进行最终自定义迁移。在这里,我们将执行“状态”操作,仅删除
cars.Tires
模型。仅因为cars.Tires
的数据库表已被重命名,所以它才处于状态。这将清除剩余的Django状态。刚刚将两个模型从旧应用程序
移动到新应用程序
,但是FK引用在应用程序x
和应用程序y
的一些模型中,而不是旧应用程序
的模型中
在这种情况下,请按照Nostalg.io提供的步骤进行操作,如下所示:
#cars/migrations/0002...py :
class AlterModelTableF( migrations.AlterModelTable):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
print( 'nothing back on', app_label, self.name, self.table)
class Migration(migrations.Migration):
dependencies = [
('cars', '0001_initial'),
]
database_operations= [
AlterModelTableF( 'tires', 'tires_tires' ),
]
operations = [
migrations.SeparateDatabaseAndState( database_operations= database_operations)
]
#cars/migrations/0004...py :
class AlterModelTableR( migrations.AlterModelTable):
def database_forwards(self, app_label, schema_editor, from_state, to_state):
print( 'nothing forw on', app_label, self.name, self.table)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
super().database_forwards( app_label, schema_editor, from_state, to_state)
class Migration(migrations.Migration):
dependencies = [
('cars', '0003_auto_20150603_0630'),
]
# This needs to be a state-only operation because the database model was renamed, and no longer exists according to Django.
state_operations = [
migrations.DeleteModel(
name='Tires',
),
]
database_operations= [
AlterModelTableR( 'tires', 'tires_tires' ),
]
operations = [
# After this state operation, the Django DB state should match the actual database structure.
migrations.SeparateDatabaseAndState( state_operations=state_operations,
database_operations=database_operations)
]
- 将模型从
移动到旧应用程序
,然后更新整个公司的新应用程序
语句导入
def migrate_model(apps, schema_editor): old_model = apps.get_model('old_app', 'MovingModel') new_model = apps.get_model('new_app', 'MovingModel') for mod in old_model.objects.all(): mod.__class__ = new_model mod.save() class Migration(migrations.Migration): dependencies = [ ('new_app', '0006_auto_20171027_0213'), ] operations = [ migrations.RunPython(migrate_model), migrations.DeleteModel( name='MovingModel', ), ]
class MigrateOrCreateTable(migrations.CreateModel): def __init__(self, source_table, dst_table, *args, **kwargs): super(MigrateOrCreateTable, self).__init__(*args, **kwargs) self.source_table = source_table self.dst_table = dst_table def database_forwards(self, app_label, schema_editor, from_state, to_state): table_exists = self.source_table in schema_editor.connection.introspection.table_names() if table_exists: with schema_editor.connection.cursor() as cursor: cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table)) else: return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state) class Migration(migrations.Migration): dependencies = [ ('myapp', '0002_some_migration'), ] operations = [ MigrateOrCreateTable( source_table='old_app_mymodel', dst_table='myapp_mymodel', name='MyModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=18)) ], ), ]
migrations.AlterModelTable('historicalmodelname', 'newapp_historicalmodelname'),
migrations.DeleteModel(name='HistoricalModleName'),
#cars/migrations/0002...py : class AlterModelTableF( migrations.AlterModelTable): def database_backwards(self, app_label, schema_editor, from_state, to_state): print( 'nothing back on', app_label, self.name, self.table) class Migration(migrations.Migration): dependencies = [ ('cars', '0001_initial'), ] database_operations= [ AlterModelTableF( 'tires', 'tires_tires' ), ] operations = [ migrations.SeparateDatabaseAndState( database_operations= database_operations) ] #cars/migrations/0004...py : class AlterModelTableR( migrations.AlterModelTable): def database_forwards(self, app_label, schema_editor, from_state, to_state): print( 'nothing forw on', app_label, self.name, self.table) def database_backwards(self, app_label, schema_editor, from_state, to_state): super().database_forwards( app_label, schema_editor, from_state, to_state) class Migration(migrations.Migration): dependencies = [ ('cars', '0003_auto_20150603_0630'), ] # This needs to be a state-only operation because the database model was renamed, and no longer exists according to Django. state_operations = [ migrations.DeleteModel( name='Tires', ), ] database_operations= [ AlterModelTableR( 'tires', 'tires_tires' ), ] operations = [ # After this state operation, the Django DB state should match the actual database structure. migrations.SeparateDatabaseAndState( state_operations=state_operations, database_operations=database_operations) ]
rm cars/migrations/* ./manage.py makemigrations ./manage.py migrate --fake-initial
class M(models.Model): a = models.ForeignKey(B, on_delete=models.CASCADE) b = models.IntegerField() class Meta: db_table = "new_M"
operations = [ SeparateDatabaseAndState([], [ migrations.CreateModel(...), ... ]), ]