Django从模型CharField中引入一个外键



我已经将10000多条记录从旧的mySQL数据库迁移到Django/sqlite。在我的旧mysql模式的Song表中,artist字段不是一个1对多字段,而只是一个mysqlvarchar字段。在我的新Django模型中,我将艺术家字段转换为ForeignKey,并使用temp_artist临时存储旧数据库中的艺术家名称。

如何基于temp_artist字段创建每个Song实例的艺术家foreignkey?我假设我应该使用经理的get_or_create方法,但在哪里以及如何编写代码?

我下面的型号:

class Artist (models.Model):
name = models.CharField(max_length=100)
class Song (models.Model):    
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.CASCADE, verbose_name="Artist")
temp_artist = models.CharField(null=True, blank=True, max_length=100)
title = models.CharField(max_length=100, verbose_name="Title")
duration = models.DurationField(null=True, blank=True, verbose_name="Duration")

您可以编写一个自定义管理命令来执行此逻辑。这些文档提供了关于如何设置它的良好说明。你的命令代码看起来像这样:

# e.g., migrateauthors.py
from django.core.management.base import BaseCommand
from myapp import models
class Command(BaseCommand):
help = 'Migrate authors from old schema'
def handle(self, *args, **options):
for song in myapp.models.Song.objects.all():
song.artist, _ = models.Artist.objects.get_or_create(name=song.temp_artist)
song.save()

然后,您只需使用manage.py migrateauthors运行管理命令。完成并验证后,可以从模型中删除临时字段。

由于目前没有可用的外键,因此必须深入到raw_sql。如果您仍然使用mysql,那么您可以使用UPDATE JOIN语法。但不幸的是,Sqlite不支持UPDATE JOIN。

幸运的是,您只有几千行,因此可以对它们进行迭代并单独更新每一行。

raw_query = '''SELECT s.*, a.id as fkid 
FROM myapp_song s 
INNER JOIN myapp_artist a on s.temp_artist = a.name'''
for song in Song.objects.raw(raw_query)
song.artist_id = s.fkid
song.save()

这可能需要几分钟才能完成,因为您没有temp_artist和name的索引。请注意用您的应用程序的实际名称替换myapp。

第1版:

虽然Sqlite没有更新JOIN,但它确实允许您使用子查询设置值。因此,这也将起作用。

UPDATE myapp_song set artist_id = 
(SELECT id from myapp_artist WHERE name = myapp_song.temp_artist)

在sqlite控制台或GUI中键入它。请确保用您自己的应用程序名称替换myapp这将非常快速,因为这是一个单独的查询。所有其他解决方案,包括我在这个答案中的备选解决方案,都涉及10000个查询。

编辑2

如果你的Artist表目前是空的,那么在你完成所有这些之前,你必须填充它,这里有一个简单的查询,可以进行

INSERT INTO stackoverflow_artist(name)
SELECT distinct temp_artist from stackoverflow_song

请注意,Artist.name 上应该有一个唯一的索引

最新更新