python

超轻量级php框架startmvc

Django 表单模型选择框如何使用分组

更新时间:2020-07-03 05:18 作者:startmvc
起步Django表单中有两种字段类型可以使用选择框:ChoiceField和ModelChoiceField。对于ChoiceField的基

起步

Django 表单中有两种字段类型可以使用选择框: ChoiceFieldModelChoiceField

对于 ChoiceField 的基本使用是:


class ExpenseForm(forms.Form):
 CHOICES = (
 (11, 'Credit Card'),
 (12, 'Student Loans'),
 (13, 'Taxes'),
 (21, 'Books'),
 (22, 'Games'),
 (31, 'Groceries'),
 (32, 'Restaurants'),
 )
 date = forms.DateField()
 category = forms.ChoiceField(choices=CHOICES)

它能渲染出:

使用分组下拉框

还可以使用如下方式生成 <optgourp> 标签:


class ExpenseForm(forms.Form):
 CHOICES = (
 ('Debt', (
 (11, 'Credit Card'),
 (12, 'Student Loans'),
 (13, 'Taxes'),
 )),
 ('Entertainment', (
 (21, 'Books'),
 (22, 'Games'),
 )),
 ('Everyday', (
 (31, 'Groceries'),
 (32, 'Restaurants'),
 )),
 )
 date = forms.DateField()
 category = forms.ChoiceField(choices=CHOICES)

能够渲染为:

分组模型下拉框

如果使用的是 ModelChoiceField ,那抱歉,Django本身没有提供解决方案。

在 https://code.djangoproject.com/ticket/27331 中提供了一个很好的解决方案。

首先为需要分类的类型创建模型,在另一个模型中用外键关联它:


from django.db import models

class Category(models.Model):
 name = models.CharField(max_length=30)
 parent = models.ForeignKey('Category', on_delete=models.CASCADE, null=True)

 def __str__(self):
 return self.name

class Expense(models.Model):
 amount = models.DecimalField(max_digits=10, decimal_places=2)
 date = models.DateField()
 category = models.ForeignKey(Category, on_delete=models.CASCADE)

 def __str__(self):
 return self.amount

其次,创建一个新的表单 Field 类型:


from functools import partial
from itertools import groupby
from operator import attrgetter

from django.forms.models import ModelChoiceIterator, ModelChoiceField

class GroupedModelChoiceIterator(ModelChoiceIterator):
 def __init__(self, field, groupby):
 self.groupby = groupby
 super().__init__(field)

 def __iter__(self):
 if self.field.empty_label is not None:
 yield ("", self.field.empty_label)
 queryset = self.queryset
 # Can't use iterator() when queryset uses prefetch_related()
 if not queryset._prefetch_related_lookups:
 queryset = queryset.iterator()
 for group, objs in groupby(queryset, self.groupby):
 yield (group, [self.choice(obj) for obj in objs])

class GroupedModelChoiceField(ModelChoiceField):
 def __init__(self, *args, choices_groupby, **kwargs):
 if isinstance(choices_groupby, str):
 choices_groupby = attrgetter(choices_groupby)
 elif not callable(choices_groupby):
 raise TypeError('choices_groupby must either be a str or a callable accepting a single argument')
 self.iterator = partial(GroupedModelChoiceIterator, groupby=choices_groupby)
 super().__init__(*args, **kwargs)

最后,在表单中可以如下进行使用:


from django import forms
from .fields import GroupedModelChoiceField
from .models import Category, Expense

class ExpenseForm(forms.ModelForm):
 category = GroupedModelChoiceField(
 queryset=Category.objects.exclude(parent=None), 
 choices_groupby='parent'
 )

 class Meta:
 model = Expense
 fields = ('amount', 'date', 'category')

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。