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

时间:2017-09-15 02:11:48

标签: python django python-3.x

我目前正在尝试使用WoTC提供的Django和SRD材料创建一个DnD 5e角色创建者。这是我第一次使用Django,我正在学习它。我已经遇到了一些挑战,这个挑战已经让我看了几天。我已经研究过这个问题,在应用了多种技术之后我认为可能会有所帮助,但我的运气有限。我的问题是:

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

models.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'

views.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})

forms.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 App :)。也是我的第一个Stack Overflow问题,所以谢谢:)

1 个答案:

答案 0 :(得分:1)

对于任何想知道的人,我使用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:

models.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字段,当用户选择Race时会动态更新。