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

如何利用Django Through模型创建状态独立的敌人实例?

问题描述

在Django项目中设计战斗系统:

  • 战斗(Battle)包含玩家(Player)和敌人(Enemy)
  • 玩家与战斗是简单多对多关系
  • 敌人需要通过Through模型关联,因为同一类敌人(如哥布林)的数据库条目是模板,但每场战斗中的每个敌人需要有独立的生命值、法力值等状态

当前已实现的简化模型:

class Battle(models.Model):
    players = models.ManyToManyField(Player)
    enemies = models.ManyToManyField(Enemy, through=EnemyThroughModel)

问题:后台关联的敌人都指向同一个Enemy实例,修改其中一个的状态会导致所有关联实例的状态同步变化,无法实现独立状态。

解决方案

核心思路是把Enemy作为基础模板存储通用属性,将战斗中敌人的当前状态放在Through模型中,而非修改原Enemy实例。

1. 调整模型结构

基础Enemy模板模型

只存储敌人的通用属性(名称、基础血量/法力等),不存当前状态:

class Enemy(models.Model):
    name = models.CharField(max_length=50)
    base_hp = models.IntegerField()  # 基础生命值
    base_mp = models.IntegerField()  # 基础法力值
    # 其他通用属性,比如攻击、防御等

    def __str__(self):
        return self.name

改造Through模型

添加当前状态字段,并关联Battle和Enemy,同时添加初始化逻辑保证状态从模板继承:

class EnemyThroughModel(models.Model):
    battle = models.ForeignKey(Battle, on_delete=models.CASCADE)
    enemy_template = models.ForeignKey(Enemy, on_delete=models.CASCADE)
    current_hp = models.IntegerField()
    current_mp = models.IntegerField()

    def save(self, *args, **kwargs):
        # 首次保存时,从模板初始化当前状态
        if not self.pk:
            self.current_hp = self.enemy_template.base_hp
            self.current_mp = self.enemy_template.base_mp
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.enemy_template.name} (战斗: {self.battle.id})"

更新Battle模型

修改多对多关联的字段映射:

class Battle(models.Model):
    players = models.ManyToManyField(Player)
    enemies = models.ManyToManyField(Enemy, through=EnemyThroughModel, through_fields=('battle', 'enemy_template'))

    def __str__(self):
        return f"战斗 {self.id}"

2. 后台配置优化

在admin.py中注册模型并设置内联显示,方便在战斗页面直接管理敌人状态:

from django.contrib import admin
from .models import Battle, Enemy, EnemyThroughModel

class EnemyThroughInline(admin.TabularInline):
    model = EnemyThroughModel
    extra = 1  # 默认显示1个空白添加条目

@admin.register(Battle)
class BattleAdmin(admin.ModelAdmin):
    inlines = [EnemyThroughInline]
    list_display = ('id',)

@admin.register(Enemy)
class EnemyAdmin(admin.ModelAdmin):
    list_display = ('name', 'base_hp', 'base_mp')

@admin.register(EnemyThroughModel)
class EnemyThroughAdmin(admin.ModelAdmin):
    list_display = ('enemy_template', 'battle', 'current_hp', 'current_mp')

3. 使用示例

创建战斗并添加敌人时,每个EnemyThroughModel实例都会基于Enemy模板生成独立状态,修改单个实例不会影响其他:

# 创建哥布林模板
goblin = Enemy.objects.create(name="哥布林", base_hp=100, base_mp=20)
# 创建战斗
battle = Battle.objects.create()
# 向战斗中添加两个哥布林
EnemyThroughModel.objects.create(battle=battle, enemy_template=goblin)
EnemyThroughModel.objects.create(battle=battle, enemy_template=goblin)
# 修改第一个哥布林的血量
first_goblin = battle.enemythroughmodel_set.first()
first_goblin.current_hp = 50
first_goblin.save()
# 第二个哥布林的血量仍为初始的100,二者状态完全独立

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 17:48:21