Skip to main content
 首页 » 编程设计

python之根据 Django 中先前的模型选择限制表单字段选择

2025年02月15日49zhengyun_ustc

我目前正在尝试使用 Django 和 WoTC 提供的 SRD Material 创建 DnD 5e 角色创建器。这是我第一次使用 Django,我是边学边学的。几天来,我遇到了一些挑战,这些挑战让我陷入困境。我研究了这个问题,在应用了我认为可能有帮助的多种技术之后,我的运气有限。我的问题是:

我有许多代表英雄、种族、子种族、类(Class)、背景等的模型。我希望能够根据用户事先选择的种族来限制用户选择子种族的能力。 到目前为止我有这个:

模型.py
class Race(models.Model): 
    race_name = models.CharField(max_length=200) 
    race_size = models.CharField( 
        max_length=2, choices=SIZE_CHOICE, default='M') 
    race_speed = models.IntegerField( 
        default=30) 
    race_lang = models.CharField(max_length=200, null=True, blank=True) 
    race_str = models.IntegerField(default=0, null=True, blank=True) 
    race_dex = models.IntegerField(default=0, null=True, blank=True) 
    race_con = models.IntegerField(default=0, null=True, blank=True) 
    race_int = models.IntegerField(default=0, null=True, blank=True) 
    race_wis = models.IntegerField(default=0, null=True, blank=True) 
    race_cha = models.IntegerField(default=0, null=True, blank=True) 
    skill_spend = models.IntegerField(default=0, null=True, blank=True) 
    race_extra = models.TextField(max_length=2000, blank=True, null=True) 
    race_source = models.CharField(max_length=200, blank=True) 
 
    def __str__(self): 
        return self.race_name 
 
    class Meta: 
        verbose_name = 'Race' 
        verbose_name_plural = 'Races' 
 
 
class Subrace(models.Model): 
    sub_name = models.CharField(max_length=200) 
    sub_size = models.CharField( 
        max_length=2, choices=SIZE_CHOICE, default='M', null=True) 
    sub_speed = models.IntegerField( 
        default=30, null=True) 
    sub_lang = models.CharField(max_length=200, null=True, blank=True) 
    sub_str = models.IntegerField(default=0, null=True, blank=True) 
    sub_dex = models.IntegerField(default=0, null=True, blank=True) 
    sub_con = models.IntegerField(default=0, null=True, blank=True) 
    sub_int = models.IntegerField(default=0, null=True, blank=True) 
    sub_wis = models.IntegerField(default=0, null=True, blank=True) 
    sub_cha = models.IntegerField(default=0, null=True, blank=True) 
    sub_extra = models.TextField(max_length=2000, null=True, blank=True) 
    sub_parent = models.ForeignKey(Race, on_delete=models.CASCADE, null=True) 
 
    def __str__(self): 
        return self.sub_name 
 
    class Meta: 
        verbose_name = 'Subrace' 
        verbose_name_plural = 'Subraces' 
 
class Hero(models.Model): 
 
    def roll_stats(): 
        d6 = die.Dice(6) 
        list_stats = d6.roll(4) 
        list_stats.sort() 
        add = sum(list_stats[1:4]) 
        return add 
 
    hero_name = models.CharField(max_length=200) 
    author = models.ForeignKey(User, null=True) 
    hero_subrace = models.ForeignKey( 
        Subrace, on_delete=models.CASCADE, null=True, blank=True) 
    pub_date = models.DateTimeField('date published', blank=True, null=True) 
    hero_klass = models.ForeignKey(Klass, on_delete=models.CASCADE, null=True) 
    hero_race = models.ForeignKey(Race, on_delete=models.CASCADE) 
    background = models.ForeignKey( 
        Background, on_delete=models.CASCADE, null=True) 
    health = models.IntegerField(blank=True, null=True) 
    hero_exp = models.IntegerField(default=0, null=True) 
    hero_alignment = models.ForeignKey(Alignment, blank=True, null=True) 
    hero_str = models.IntegerField(default=roll_stats, null=True, blank=True) 
    hero_dex = models.IntegerField(default=roll_stats, null=True, blank=True) 
    hero_con = models.IntegerField(default=roll_stats, null=True, blank=True) 
    hero_int = models.IntegerField(default=roll_stats, null=True, blank=True) 
    hero_wis = models.IntegerField(default=roll_stats, null=True, blank=True) 
    hero_cha = models.IntegerField(default=roll_stats, null=True, blank=True) 
 
    def save(self, *args, **kwargs): 
        "Returns a hero's hp" 
        die_str = str(self.hero_klass.hit_dice) 
        die_nums = die_str.split("d") 
        die_val = int(die_nums[1]) 
        die_roll = int(die_nums[0]) 
        hp_die = die.Dice(die_val) 
        results = hp_die.roll(die_roll) 
        self.health = sum(results) 
        super(Hero, self).save(*args, **kwargs) 
 
    def __str__(self): 
        return self.hero_name 
 
    def get_absolute_url(self): 
        return reverse('hero.views.detail', args=[str(self.id)]) 
 
    class Meta: 
        verbose_name = 'Hero' 
        verbose_name_plural = 'Heroes' 

View .py
def new_hero(request): 
    user = request.user 
    if request.method == "POST": 
        form = HeroForm(request.POST) 
        if form.is_valid(): 
            hero = form.save(commit=False) 
            hero.author = request.user 
            hero.save() 
            return redirect('detail', hero.pk) 
    else: 
        form = HeroForm() 
    return render(request, 'new_hero.html', {'form': form, 'user': user}) 

表单.py
class HeroForm(forms.ModelForm): 
 
    class Meta: 
        model = Hero 
        fields = ['hero_name', 'hero_race', 'hero_subrace', 
                  'hero_klass', 'hero_exp', 'health', 'background', 
                  'hero_str', 'hero_dex', 'hero_con', 'hero_int', 
                  'hero_wis', 'hero_cha', 'hero_alignment'] 
 
    def __init__(self, *args, **kwargs): 
        super(HeroForm, self).__init__(*args, **kwargs) 
        for fieldname in ['hero_str', 'hero_dex', 'hero_con', 'hero_int', 'hero_wis', 'hero_cha']: 
            self.fields[fieldname].disabled = True 
        race = Race.objects.all() 
        for name in race: 
            self.fields['hero_subrace'].queryset = Subrace.objects.filter(sub_parent=name) 

我尝试了几种不同的技术,但这就是我现在的处境。这个:

for name in race: 
            self.fields['hero_subrace'].queryset = Subrace.objects.filter(sub_parent=name) 

是我最近添加到我的应用程序中的内容。在英雄创建屏幕上,我看到了一个空白的选择框,而不是没有循环或查询集的完整无限制列表。

基本上,我希望有人对我可能忽略的方法、我错过的或根本没有找到的方法提出一些建议。也请随意批评其余代码,就像我说这是我的第一个 Django 应用程序 :)。这也是我的第一个 Stack Overflow 问题,非常感谢 :)

请您参考如下方法:

对于任何想知道的人,我使用了 django-smart-selects解决我的问题。

base.html

<script type="text/javascript" src="{% static 'smart-selects/admin/js/chainedfk.js' %}"></script> 
<script type="text/javascript" src="{% static 'smart-selects/admin/js/bindfields.js' %}"></script> 

我将上面的 html 添加到我的 {% load staticfiles %} 调用中。

并更改了 models.py:

模型.py
from smart_selects.db_fields import ChainedForeignKey 
class Hero(models.Model): 
    .... 
    race = models.ForeignKey(Race, on_delete=models.CASCADE) 
    subrace = ChainedForeignKey(Subrace, 
                                chained_field="race", 
                                chained_model_field="race", 
                                show_all=False, 
                                auto_choose=True, 
                                blank=True, 
                                null=True) 

现在我有一个 subrace 字段,它会在用户选择比赛时动态更新。