我有两个类,其中一个是另一个类的后代,我想让它们成为来自同一基类的兄弟类。
在:
from django.db import models
class A(models.Model):
name = models.CharField(max_length=10)
class B(models.Model):
title = models.CharField(max_length=10)
后:
from django.db import models
class Base(models.Model):
name = models.CharField(max_length=10)
class A(Base):
pass
class B(Base):
title = models.CharField(max_length=10)
当我生成架构迁移时,这是输出,包括我对问题的回答:
+ Added model basetest.Base
? The field 'B.a_ptr' does not have a default specified, yet is NOT NULL.
? Since you are removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? 3. Disable the backwards migration by raising an exception.
? Please select a choice: 3
- Deleted field a_ptr on basetest.B
? The field 'B.base_ptr' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 37
+ Added field base_ptr on basetest.B
? The field 'A.id' does not have a default specified, yet is NOT NULL.
? Since you are removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? 3. Disable the backwards migration by raising an exception.
? Please select a choice: 3
- Deleted field id on basetest.A
? The field 'A.name' does not have a default specified, yet is NOT NULL.
? Since you are removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? 3. Disable the backwards migration by raising an exception.
? Please select a choice: 3
- Deleted field name on basetest.A
? The field 'A.base_ptr' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 73
+ Added field base_ptr on basetest.A
Created 0002_auto__add_base__del_field_b_a_ptr__add_field_b_base_ptr__del_field_a_i.py. You can now apply this migration with: ./manage.py migrate basetest
我不知道如何回答有关B.base_ptr和A.base_ptr的默认值的问题。我给出的任何常量都会导致迁移在运行时失败,输出结果为:
FATAL ERROR - The following SQL query failed: CREATE TABLE "_south_new_basetest_a" ()
The error was: near ")": syntax error
RuntimeError: Cannot reverse this migration. 'B.a_ptr' and its values cannot be restored.
这是我顺便使用sqlite3的结果。使用Postgres就是这样的:
FATAL ERROR - The following SQL query failed: ALTER TABLE "basetest_a" ADD COLUMN "base_ptr_id" integer NOT NULL PRIMARY KEY DEFAULT 73;
The error was: could not create unique index "basetest_a_pkey"
DETAIL: Key (base_ptr_id)=(73) is duplicated.
Error in migration: basetest:0002_auto__add_base__del_field_b_a_ptr__add_field_b_base_ptr__del_field_a_i
IntegrityError: could not create unique index "basetest_a_pkey"
DETAIL: Key (base_ptr_id)=(73) is duplicated.
我应该使用什么值来使base_ptr进行此迁移?谢谢!
答案 0 :(得分:10)
如果base
无法自行实例化,您可以使用abstract = True
道具轻松解决问题{.1}}。
示例代码:
class Meta
答案 1 :(得分:7)
您可以分阶段完成此任务。
阶段1:在代码中创建“基础”模型。在A和B模型上,将base_ptr
作为可空的FK添加到Base(名称base_ptr
通过小写类名Base
来生成,相应地调整您的名称)。在新列上指定db_column='base_ptr'
,这样就不会添加_id
后缀。不要改变父母身份:保持B
为A
和A
之前的孩子(Base
还没有子课程)。添加迁移以进行相应的数据库更改,然后运行它。
阶段2:创建数据迁移,复制相关数据。您应该将所有A
数据复制到Base
,删除多余的A
记录(提供B
个实例的记录),以及剩余记录(A
并B
)将ID复制到base_ptr
。请注意,子类B
使用两个表 - 其id
字段来自A
的表,并且在其自己的表中有一个字段a_ptr
,它是一个FK到A
- 因此,如果您将值从a_ptr
复制到base_ptr
,则更新操作会更有效。确保在复制到Base
表后复制到base_ptr,这样就不会违反FK约束。
阶段3:现在再次更改模型 - 删除显式base_ptr
FK并按照您喜欢的方式更改父项,并创建第三次迁移(自动模式迁移)。请注意,将父级设置为Base
隐式定义了一个不可为空的base_ptr
字段,因此对于base_ptr
字段,您只是将可空字段更改为不可为空,而不是需要默认值。
我们仍然会要求您提供a_ptr
的默认值 - 从父B
更改为A
时删除的从A
到Base
的隐式FK a_ptr
;向后迁移需要默认值。您可以执行一些无法进行向后迁移的操作,或者,如果您确实要支持它,请向B
添加一个明确的可为空base_ptr
,就像之前使用的{{1}}列一样。然后可以在第四次迁移中删除此可以为空的列。
答案 2 :(得分:0)
或者您可以通过在元数据中添加以下内容来将类指定为抽象类:
class Meta:
abstract =True