ygr*_*oel 11 django django-models
我创建了一个带有电子邮件地址的模型作为自定义主键,如下所示:
email = models.EmailField(max_length=255, primary_key=True,)
Run Code Online (Sandbox Code Playgroud)
现在我意识到在我的情况下这不是一个好主意,我想回到自动生成的id字段作为主键.
这该怎么做?我以不同的方式尝试了这一点,但都失败了.我正在使用带有Python 3.4.3的Django 1.10.4和一个SQLite数据库.
python manage.py makemigrations抱怨:您试图在没有默认值的情况下向用户添加不可为空的字段"id"; 我们不能那样做(数据库需要一些东西来填充现有的行).
如果我指定0为默认值,则python manage.py migrate失败django.db.utils.IntegrityError: UNIQUE constraint failed: login_user.id
根据此帖子将主键字段更改为唯一字段,我尝试手动添加自动字段,如下所示:
id = models.AutoField()
现在python manage.py makemigrations失败了:
login.User.id: (fields.E100) AutoFields must set primary_key=True.
Run Code Online (Sandbox Code Playgroud)
如果我按照错误消息的建议执行操作,我会遇到与第一次尝试时相同的问题:缺少默认值.
makemigrations工作正常但migrate失败并带有回溯和此错误:django.db.utils.OperationalError: duplicate column name: id 它似乎正在尝试创建一个额外的"id"列,不知道为什么.这样做的正确方法是什么?此外,如果成功,是否会正确更新引用我的用户的ForeignKey字段?
我自己也遇到过这个问题,并最终编写了一个可重用的(不过是 MySQL 特定的)迁移。您可以在此存储库中找到代码。我也在我的博客中写过相关内容。
概括来说,所采取的步骤是:
像这样修改你的模型类:
class Something(models.Model):
email = models.EmailField(max_length=255, unique=True)
Run Code Online (Sandbox Code Playgroud)沿着这些线添加新的迁移:
app_name = 'app'
model_name = 'something'
related_model_name = 'something_else'
model_table = '%s_%s' % (app_name, model_name)
pivot_table = '%s_%s_%ss' % (app_name, related_model_name, model_name)
fk_name, index_name = None, None
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name=model_name,
name='id',
field=models.IntegerField(null=True),
preserve_default=True,
),
migrations.RunPython(do_most_of_the_surgery),
migrations.AlterField(
model_name=model_name,
name='id',
field=models.AutoField(
verbose_name='ID', serialize=False, auto_created=True,
primary_key=True),
preserve_default=True,
),
migrations.AlterField(
model_name=model_name,
name='email',
field=models.EmailField(max_length=255, unique=True),
preserve_default=True,
),
migrations.RunPython(do_the_final_lifting),
]
Run Code Online (Sandbox Code Playgroud)
在哪里
def do_most_of_the_surgery(apps, schema_editor):
models = {}
Model = apps.get_model(app_name, model_name)
# Generate values for the new id column
for i, o in enumerate(Model.objects.all()):
o.id = i + 1
o.save()
models[o.email] = o.id
# Work on the pivot table before going on
drop_constraints_and_indices_in_pivot_table()
# Drop current pk index and create the new one
cursor.execute(
"ALTER TABLE %s DROP PRIMARY KEY" % model_table
)
cursor.execute(
"ALTER TABLE %s ADD PRIMARY KEY (id)" % model_table
)
# Rename the fk column in the pivot table
cursor.execute(
"ALTER TABLE %s "
"CHANGE %s_id %s_id_old %s NOT NULL" %
(pivot_table, model_name, model_name, 'VARCHAR(255)'))
# ... and create a new one for the new id
cursor.execute(
"ALTER TABLE %s ADD COLUMN %s_id INT(11)" %
(pivot_table, model_name))
# Fill in the new column in the pivot table
cursor.execute("SELECT id, %s_id_old FROM %s" % (model_name, pivot_table))
for row in cursor:
id, key = row[0], row[1]
model_id = models[key]
inner_cursor = connection.cursor()
inner_cursor.execute(
"UPDATE %s SET %s_id=%d WHERE id=%d" %
(pivot_table, model_name, model_id, id))
# Drop the old (renamed) column in pivot table, no longer needed
cursor.execute(
"ALTER TABLE %s DROP COLUMN %s_id_old" %
(pivot_table, model_name))
def do_the_final_lifting(apps, schema_editor):
# Create a new unique index for the old pk column
index_prefix = '%s_id' % model_table
new_index_prefix = '%s_email' % model_table
new_index_name = index_name.replace(index_prefix, new_index_prefix)
cursor.execute(
"ALTER TABLE %s ADD UNIQUE KEY %s (%s)" %
(model_table, new_index_name, 'email'))
# Finally, work on the pivot table
recreate_constraints_and_indices_in_pivot_table()
Run Code Online (Sandbox Code Playgroud)
这种情况很难解决,特别是在 sqlite 上,它实际上甚至没有真正的 ALTER TABLE 语句
SQLite 支持 ALTER TABLE 的有限子集。SQLite 中的 ALTER TABLE 命令允许用户重命名表或向现有表添加新列。
大多数类型,django 通过临时表进行更改。所以你也可以这样做
第 1 步:创建一个新模型,与
class TempModel(models.Model):
email = models.EmailField(max_length=255)
# other fields from your existing model
Run Code Online (Sandbox Code Playgroud)
请注意,您不需要显式声明主键字段。只需在电子邮件字段中将其关闭就足够了。
第2步:进行迁移并迁移
第 3 步:打开您最喜欢的数据库客户端并执行以下操作:
INSERT INTO myapp_tempmodel(fields,....) SELECT * FROM myapp_oldmodel
Run Code Online (Sandbox Code Playgroud)
步骤4:删除旧表,进行迁移并迁移
步骤5:重命名临时表,进行迁移并迁移