我需要将我的一个Django模型中的字段类型从CharField
更改为ForeignKey
。这些字段已经填充了数据,所以我想知道这样做的最佳或正确方法是什么。我可以更新字段类型并进行迁移,还是有任何可能的问题'要注意? N.B :我只使用vanilla Django管理操作(makemigrations
和migrate
),而不是南方。
答案 0 :(得分:45)
这可能是您想要进行多阶段迁移的情况。我对此的建议如下所示。
首先,让我们假设这是您的初始模型,在名为discography
的应用程序中:
from django.db import models
class Album(models.Model):
name = models.CharField(max_length=255)
artist = models.CharField(max_length=255)
现在,您意识到您想要为艺术家使用ForeignKey。好吧,如上所述,这不仅仅是一个简单的过程。它必须分几步完成。
步骤1,为ForeignKey添加一个新字段,确保将其标记为null:
from django.db import models
class Album(models.Model):
name = models.CharField(max_length=255)
artist = models.CharField(max_length=255)
artist_link = models.ForeignKey('Artist', null=True)
class Artist(models.Model):
name = models.CharField(max_length=255)
...并为此更改创建迁移。
./manage.py makemigrations discography
第2步,填充新字段。为此,您必须创建一个空迁移。
./manage.py makemigrations --empty --name transfer_artists discography
完成此空迁移后,您需要向其添加一个RunPython
操作才能链接您的记录。在这种情况下,它可能看起来像这样:
def link_artists(apps, schema_editor):
Album = apps.get_model('discography', 'Album')
Artist = apps.get_model('discography', 'Artist')
for album in Album.objects.all():
artist, created = Artist.objects.get_or_create(name=album.artist)
album.artist_link = artist
album.save()
既然您的数据已转移到新字段,您实际上可以完成并保留所有内容,使用新字段表示所有内容。或者,如果您想进行一些清理,则需要再创建两次迁移。
首次迁移时,您需要删除原始字段artist
。对于第二次迁移,请将新字段artist_link
重命名为artist
。
这是通过多个步骤完成的,以确保Django正确识别操作。你可以手动创建一个迁移来处理这个问题,但我会留给你解决。
答案 1 :(得分:4)
除了Joey的答案之外,还有针对Django 2.2.11的详细步骤。
这是我的用例中的模型,由Company
和Employee
模型组成。我们必须将designation
转换为外键字段。应用名称为core
class Company(CommonFields):
name = models.CharField(max_length=255, blank=True, null=True
class Employee(CommonFields):
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
designation = models.CharField(max_length=100, blank=True, null=True)
第1步
在Employee中创建外键designation_link
并将其标记为null = True
class Designation(CommonFields):
name = models.CharField(max_length=255)
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
class Employee(CommonFields):
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
designation = models.CharField(max_length=100, blank=True, null=True)
designation_link = models.ForeignKey("Designation", on_delete=models.CASCADE, blank=True, null=True)
第2步
创建空迁移。使用命令:
python app_code/manage.py makemigrations --empty --name transfer_designations core
这将在migrations
目录中创建以下文件。
# Generated by Django 2.2.11 on 2020-04-02 05:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20200402_1119'),
]
operations = [
]
第3步
使用循环所有Employees
的函数填充空迁移,创建一个Designation
并将其链接到Employee
。
在我的用例中,每个Designation
也都链接到一个Company
。这意味着Designation
可能包含两行用于“经理”,一行用于公司A,另一行用于公司B。
最终迁移看起来像这样:
# core/migrations/0007_transfer_designations.py
# Generated by Django 2.2.11 on 2020-04-02 05:56
from django.db import migrations
def link_designation(apps, schema_editor):
Employee = apps.get_model('core', 'Employee')
Designation = apps.get_model('core', 'Designation')
for emp in Employee.objects.all():
if(emp.designation is not None and emp.company is not None):
desig, created = Designation.objects.get_or_create(name=emp.designation, company=emp.company)
emp.designation_link = desig
emp.save()
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20200402_1119'),
]
operations = [
migrations.RunPython(link_designation),
]
第4步
最后使用以下命令运行此迁移:
python app_code/manage.py migrate core 0007
答案 2 :(得分:2)
这是乔伊的一个很好的答案的延续。 如何将新字段重命名为原始名称?
如果该字段具有数据,则可能意味着您正在项目的其他位置使用它,因此此解决方案将为您提供一个名称不同的字段,并且您必须重构项目以使用新字段或删除该字段。旧字段并重命名新字段。
请注意,此过程不会阻止您重构代码。例如,如果您在使用带有CHOICES的CharField的地方,您正在使用 get_filename_display()访问其内容。
如果您尝试删除该字段以进行迁移,然后重命名另一个字段并进行另一个迁移,则会看到Django抱怨,因为您无法删除项目中正在使用的字段。
只需按照乔伊(Joey)的说明创建一个空迁移,然后将其放入操作中即可:
operations = [
migrations.RemoveField(
model_name='app_name',
name='old_field_name',
),
migrations.RenameField(
model_name='app_name',
old_name='old_field_name_link',
new_name='old_field_name',
),
]
然后运行迁移,您将在数据库中进行更改,但显然没有在模型中进行更改,现在是时候删除旧字段并将新的ForeignKey字段重命名为原始名称了。
我认为这样做并不是很容易,但是,只有在您完全了解自己在搞什么的时候,才做这种事情。