在Django中自动生成表单字段

9 投票
3 回答
6520 浏览
提问于 2025-04-15 14:14

我有一些模型数据,想从这些数据生成一个可以多选的表单。这个表单会为每个类别提供一个选项,而这些选项就是该类别下的技能。

models.py

class SkillCategory(models.Model):
    name = models.CharField(max_length=50)

class Skill(models.Model):
    name = models.CharField(max_length=50)
    category = models.ForeignKey(SkillCategory)

有没有办法自动生成表单字段?我知道我可以手动为每个技能类别在表单中添加一个“SkillCategory”的条目,但之所以把它做成模型,是因为这样可以自由编辑技能和技能类别。

我想做类似这样的事情:(我试过这个,但没有成功,具体的错误我记不清了……)

forms.py

class SkillSelectionForm(forms.Form):
    def __init__(*args, **kwargs):
        super(SkillSelectionForm, self).__init__(*args, **kwargs)
        for c in SkillCategory.objects.all():
            category_skills = [(pk, s.name) for s in c.skill_set.all()]
            setattr(self, c.name, forms.MultipleChoiceField(choices=category_skills, widget=forms.CheckboxSelectMultiple))

解决方案

这个方法会使用SkillCategory.name来创建一个表单字段,并将选项设置为Skill中的那些。field_name/display_name是用来避免非ASCII字符的类别名称出现问题。

forms.py

def get_categorized_skills():
    skills = {}
    for s in Skill.objects.values('pk', 'name', 'category__name').order_by('category__name'):
        if s['category__name'] not in skills.keys():
            skills[s['category__name']] = []
        skills[s['category__name']].append((s['pk'], s['name']))
    return skills

class SkillSelectionForm(forms.Form): 
    def __init__(self, *args, **kwargs):
        super(SkillSelectionForm, self).__init__(*args, **kwargs)
        skills = get_categorized_skills()
        for idx, cat in enumerate(skills.keys()):
            field_name = u'category-{0}'.format(idx)
            display_name = cat
            self.fields[field_name] = forms.MultipleChoiceField(choices=skills[cat], widget=forms.CheckboxSelectMultiple, label=display_name)

3 个回答

1

你需要的是一个表单集合(Formset)。这个表单集合会给你一组行,每一行都对应一个特定的技能。

可以查看一下表单集合的文档,还有专门讲如何为模型生成表单集合的页面。

3

好的,你不能像那样在 forms.Form 上设置字段,原因等你看看 DeclarativeFieldsMetaclass 这个东西的时候就明白了。这个东西是 forms.Form 的元类(但不是 forms.BaseForm 的元类)。虽然这个解决方案可能对你来说有点复杂,但它展示了如何动态构建表单,像这样:

base_fields = [
    forms.MultipleChoiceField(choices=[
        (pk, s.name) for s in c.skill_set.all()
    ]) for c in SkillCategory.objects.all()
]
SkillSelectionForm = type('SkillSelectionForm', (forms.BaseForm,), {'base_fields': base_fields})
1

看看如何在Django中创建动态表单,可以参考 b-list.orguswaretech.com 上的内容。我用这些例子成功地从模型中动态生成表单内容。

撰写回答