【问题标题】:Django Introduce a foreign key from a model CharFieldDjango 从模型 CharField 中引入外键
【发布时间】:2016-09-18 09:27:00
【问题描述】:

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

如何根据 temp_artist 字段创建每个 Song 实例的艺术家外键?我假设我应该使用经理的 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")

【问题讨论】:

    标签: django sqlite model


    【解决方案1】:

    您可以编写一个custom management command 来为您执行此逻辑。文档提供了有关如何设置的很好的说明。您的命令代码如下所示:

    # 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 运行管理命令。完成并验证后,您可以从模型中删除临时字段。

    【讨论】:

      【解决方案2】:

      由于您目前没有可用的外键,因此您必须深入挖掘 raw_sql。如果您仍在使用 mysql,则可以使用 UPDATE JOIN 语法。但遗憾的是 Sqlite 不支持UPDATEJOIN。

      幸运的是,您只有几千行,这使得遍历它们并单独更新每一行成为可能。

      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 替换为您自己的应用名称。 这将非常快,因为它是单个查询。包括我在这个答案中的替代解决方案在内的所有其他解决方案都涉及 10,000 个查询。

      编辑 2

      如果您的 Artist 表目前是空的,在您执行所有这些操作之前,您必须填充它,这里有一个简单的查询来完成它

      INSERT INTO stackoverflow_artist(name)
         SELECT distinct temp_artist from stackoverflow_song
      

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

      【讨论】:

      • 如果使用第二种方法,myapp_artist表目前是空的,没有数据,需要先创建艺术家姓名字段,那么此时如何从myapp_artist中SELECT id?
      • 糟糕,查询中有一个小错误。它需要一个独特的。还要注意关于独特的观察
      • 只是出于好奇,为什么当我的答案可以解释您提出的所有问题时,选择了另一个答案?
      • 糟糕,我认为检查意味着它也有效。没有看到你只能选择一个,但你的更快。
      • 谢谢。当一个答案很好时,通常会投票(我也会投票给另一个答案)。但别担心,这没什么大不了的
      猜你喜欢
      • 1970-01-01
      • 2016-05-25
      • 2020-02-05
      • 2011-12-24
      • 1970-01-01
      • 2013-01-17
      • 1970-01-01
      • 2015-03-07
      • 2019-02-06
      相关资源
      最近更新 更多