我已经将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 上应该有一个唯一的索引