You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django模型查询报错:缺失inventory_instance_id列求助

问题分析与解决:Django模型关联迁移导致查询失败

问题现象

查询Character.objects.all()时触发错误:

OperationalError: no such column: oblivionalchemy_character.inventory_instance_id

模型代码

当前oblivionalchemy.models中的定义:

class InventoryInstance(models.Model):
    character_name = models.ForeignKey('oblivionalchemy.Character', on_delete=models.CASCADE, null=True, related_name="character")
    items = models.JSONField(default=dict)
class Character(models.Model):
    user = models.ForeignKey('discordbot.DiscordUser', on_delete=models.CASCADE, null=True, related_name="characters")
    name = models.CharField(max_length=40)  
    strength = models.IntegerField(default=10)
    endurance = models.IntegerField(default=10)
    agility = models.IntegerField(default=10)
    speed = models.IntegerField(default=10)
    willpower = models.IntegerField(default=10)
    intelligence = models.IntegerField(default=10)
    personality = models.IntegerField(default=10)
    luck = models.IntegerField(default=10)
    alchemy = models.IntegerField(default=10)
    survival = models.IntegerField(default=10)
    blade = models.IntegerField(default=10)
    marksman = models.IntegerField(default=10)
    inventory_instance = models.OneToOneField('inventory.InventoryInstance', on_delete=models.CASCADE, related_name="character", null=True, blank=True)

初始迁移文件

oblivionalchemy app

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('discordbot', '0001_initial'),
        ('inventory', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='Character',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=40)),
                ('strength', models.IntegerField(default=10)),
                ('endurance', models.IntegerField(default=10)),
                ('agility', models.IntegerField(default=10)),
                ('speed', models.IntegerField(default=10)),
                ('willpower', models.IntegerField(default=10)),
                ('intelligence', models.IntegerField(default=10)),
                ('personality', models.IntegerField(default=10)),
                ('luck', models.IntegerField(default=10)),
                ('alchemy', models.IntegerField(default=10)),
                ('survival', models.IntegerField(default=10)),
                ('blade', models.IntegerField(default=10)),
                ('marksman', models.IntegerField(default=10)),
                ('inventory_instance', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='character', to='inventory.inventoryinstance')),
                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='characters', to='discordbot.user')),
            ],
        ),
    ]

discordbot app

class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='User',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('discord_id', models.CharField(blank=True, max_length=100, unique=True)),
            ],
        ),
    ]

inventory app

class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='InventoryInstance',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('items', models.JSONField(default=dict)),
            ],
        ),
        migrations.CreateModel(
            name='InventoryItem',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=40)),
                ('weight', models.IntegerField(default=0)),
                ('hitpoints', models.IntegerField(default=100)),
                ('is_default', models.BooleanField(default=False)),
                ('effects', models.JSONField(default=inventory.models.default_effects_for_item)),
            ],
        ),
    ]

问题原因

  1. 模型关联冲突:InventoryInstance定义了指向Character的ForeignKey,同时Character又定义了指向InventoryInstance的OneToOneField,且两者的related_name均为character,导致字段映射逻辑混乱。
  2. 模型归属不一致:InventoryInstance代码位于oblivionalchemy.models,但inventory app的初始迁移却创建了该模型,导致Django无法正确匹配模型与数据库表的关联。
  3. 迁移执行异常:初始迁移中虽定义了inventory_instance字段,但可能因之前的迁移操作失败,数据库中未实际创建该字段;同时先存在Character记录再添加关联,迁移未正确处理存量数据。

解决步骤

1. 修正模型关联逻辑

按照DiscordUser → Character → InventoryInstance的创建流程,清理重复关联,统一模型归属:

# 若InventoryInstance属于inventory app,移至inventory/models.py
# inventory/models.py
class InventoryInstance(models.Model):
    items = models.JSONField(default=dict)

# oblivionalchemy/models.py
class Character(models.Model):
    user = models.ForeignKey('discordbot.DiscordUser', on_delete=models.CASCADE, null=True, related_name="characters")
    # 保留原有属性
    inventory_instance = models.OneToOneField('inventory.InventoryInstance', on_delete=models.CASCADE, related_name="character", null=True, blank=True)

2. 修复迁移与数据库不一致

  • 备份数据库,避免数据丢失。
  • 若迁移已应用但字段缺失,创建空迁移手动补全字段:
    python manage.py makemigrations --empty oblivionalchemy
    
    编辑生成的迁移文件:
    from django.db import migrations, models
    
    class Migration(migrations.Migration):
        dependencies = [
            ('oblivionalchemy', '0001_initial'), # 替换为当前最新迁移编号
        ]
    
        operations = [
            migrations.AddField(
                model_name='character',
                name='inventory_instance',
                field=models.OneToOneField(blank=True, null=True, on_delete=models.CASCADE, related_name='character', to='inventory.inventoryinstance'),
            ),
        ]
    
    执行迁移:
    python manage.py migrate
    
  • 若迁移彻底混乱,可重置迁移(需备份数据):
    1. 删除各app下migrations文件夹内除__init__.py外的所有文件。
    2. 清空数据库中django_migrations表。
    3. 重新生成并执行迁移:
      python manage.py makemigrations
      python manage.py migrate
      

3. 同步存量数据

  • 关联无DiscordUser的Character记录:
    from discordbot.models import DiscordUser
    from oblivionalchemy.models import Character
    
    orphan_chars = Character.objects.filter(user__isnull=True)
    for char in orphan_chars:
        # 根据业务逻辑创建或关联DiscordUser,示例为创建临时用户
        user, _ = DiscordUser.objects.get_or_create(discord_id=f'temp_{char.id}')
        char.user = user
        char.save()
    
  • 为存量Character创建对应InventoryInstance:
    from inventory.models import InventoryInstance
    from oblivionalchemy.models import Character
    
    for char in Character.objects.all():
        if not char.inventory_instance:
            inv = InventoryInstance.objects.create(items={})
            char.inventory_instance = inv
            char.save()
    

内容的提问来源于stack exchange,提问作者Syllogism

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:50:00