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

Django中实现用户自定义Hero组合的Deck复用与统计需求

嘿,我来帮你梳理下这个问题的解决思路,毕竟刚接触Django的时候,确实容易在多对多关联和实例复用这块绕晕😉

核心问题拆解

先明确你的需求核心矛盾:

  • 用户需要自主选择4个英雄,同时关联到Match记录,还要自动生成/复用对应的Deck实例
  • 之前直接把Deck关联到Match的方案,把选择权限制成了“只能用已有Deck”,完全不符合用户自主选新组合的需求,所以得换个思路:让表单接收英雄选择,后端处理Deck的复用/创建逻辑,再关联到Match
模型调整:确保Deck组合的唯一性

首先要解决的是“相同英雄组合复用Deck”的问题,我们可以给Deck加一个唯一标识字段,通过排序后的英雄ID生成字符串,保证同一组英雄不管选择顺序如何,都对应同一个Deck实例。

修改models.py:

from django.db import models

class Hero(models.Model):
    heroname = models.CharField(max_length=20)
    summary = models.TextField(max_length=2000, null=True)

    def __str__(self):
        return self.heroname

class Deck(models.Model):
    heroes = models.ManyToManyField(Hero)
    # 新增唯一字段:存储排序后的英雄ID字符串,用于判断组合是否重复
    hero_combination = models.CharField(max_length=100, unique=True)

    def save(self, *args, **kwargs):
        # 保存前自动生成唯一标识:把关联英雄的ID升序排序后用逗号拼接
        if self.heroes.exists():
            sorted_hero_ids = sorted(self.heroes.values_list('id', flat=True))
            self.hero_combination = ','.join(map(str, sorted_hero_ids))
        super().save(*args, **kwargs)

class Match(models.Model):
    # Match关联Deck,后续统计使用次数直接基于这个关联查询
    deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
    # 可按需添加其他字段,比如比赛时间、结果等
    created_at = models.DateTimeField(auto_now_add=True)
表单调整:让用户直接选英雄

之前的DeckForm是直接操作Deck模型,现在我们需要让用户直接选英雄,所以改用普通Form(而非ModelForm),只负责验证选择数量:

修改forms.py:

from django import forms
from .models import Hero

class HeroSelectionForm(forms.Form):
    heroes = forms.ModelMultipleChoiceField(
        queryset=Hero.objects.all(),
        widget=forms.CheckboxSelectMultiple(),
        label="Select 4 Heroes"
    )

    def clean_heroes(self):
        selected_heroes = self.cleaned_data.get('heroes')
        if len(selected_heroes) != 4:
            raise forms.ValidationError("You must select exactly 4 heroes.")
        return selected_heroes
视图核心逻辑:处理Deck复用/创建+关联Match

这一步是关键,我们要在视图里接收用户选的英雄,判断对应的Deck是否存在:存在就复用,不存在就创建,最后关联到Match。

示例视图代码:

from django.shortcuts import render, redirect
from .forms import HeroSelectionForm
from .models import Deck, Match

def create_match(request):
    if request.method == 'POST':
        form = HeroSelectionForm(request.POST)
        if form.is_valid():
            selected_heroes = form.cleaned_data['heroes']
            # 生成和Deck模型一致的唯一组合标识
            sorted_hero_ids = sorted([hero.id for hero in selected_heroes])
            hero_combination = ','.join(map(str, sorted_hero_ids))

            # 复用已有Deck或创建新Deck:get_or_create会返回(实例, 是否新创建)
            deck, is_new_deck = Deck.objects.get_or_create(
                hero_combination=hero_combination,
                defaults={}
            )
            # 如果是新创建的Deck,把选中的英雄关联进去
            if is_new_deck:
                deck.heroes.add(*selected_heroes)

            # 创建Match并关联Deck
            Match.objects.create(deck=deck)
            return redirect('match_success')  # 替换成你的成功页面URL
    else:
        form = HeroSelectionForm()
    return render(request, 'create_match.html', {'form': form})
额外需求实现:Deck组合对比+使用次数统计

1. Deck组合对比

给Deck模型加个方法,方便直接对比两个Deck的英雄组合是否一致:

class Deck(models.Model):
    # ... 其他字段和方法 ...
    def get_hero_id_set(self):
        return set(self.heroes.values_list('id', flat=True))

    def has_same_combination(self, other_deck):
        # 比较两个Deck的英雄ID集合是否相同
        return self.get_hero_id_set() == other_deck.get_hero_id_set()

使用时直接调用deck1.has_same_combination(deck2)就能判断组合是否一致。

2. 特定组合使用次数统计

因为Match关联了Deck,统计非常简单:

# 统计某个Deck的使用次数
def get_deck_usage_count(target_deck):
    return Match.objects.filter(deck=target_deck).count()

# 统计任意英雄组合的使用次数(传入英雄ID列表)
def get_combination_usage_count(hero_ids):
    sorted_ids = sorted(hero_ids)
    combination_str = ','.join(map(str, sorted_ids))
    try:
        deck = Deck.objects.get(hero_combination=combination_str)
        return get_deck_usage_count(deck)
    except Deck.DoesNotExist:
        return 0
之前思路的问题总结

你之前直接把Deck关联到Match,然后让用户选Deck,本质是把“选英雄组合”变成了“选已有Deck”,完全锁死了用户创建新组合的可能。现在的方案是让用户直接选英雄,后端自动处理Deck的复用/创建,既满足了用户需求,又保留了Deck模型的统计、对比价值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:35:27