如何对 Django Select 小部件中的选择进行分组?
问题描述
是否可以在 Django 选择(下拉)小部件中创建 命名选择组,当该小部件位于从数据模型自动生成的表单上时?我可以在下面的左侧图片中创建小部件吗?
Is it possible to created named choice groups in a Django select (dropdown) widget, when that widget is on a form that is auto-generated from a data Model? Can I create the widget on the left-side picture below?
我在创建具有命名组的表单方面的第一个实验是手动完成的,如下所示:
My first experiment in creating a form with named groups, was done manually, like this:
class GroupMenuOrderForm(forms.Form):
food_list = [(1, 'burger'), (2, 'pizza'), (3, 'taco'),]
drink_list = [(4, 'coke'), (5, 'pepsi'), (6, 'root beer'),]
item_list = ( ('food', tuple(food_list)), ('drinks', tuple(drink_list)),)
itemsField = forms.ChoiceField(choices = tuple(item_list))
def GroupMenuOrder(request):
theForm = GroupMenuOrderForm()
return render_to_response(menu_template, {'form': theForm,})
# generates the widget in left-side picture
而且效果很好,在左侧创建下拉小部件,并带有命名组.
And it worked nicely, creating the dropdown widget on the left, with named groups.
然后我创建了一个具有基本相同结构的数据模型,并使用 Django 从模型自动生成表单的能力.它奏效了——从某种意义上说,它显示了所有选项.但是选项不在命名组中,到目前为止,我还没有弄清楚如何去做——如果可能的话.
I then created a data Model that had basically the same structure, and used Django's ability to auto-generate forms from Models. It worked - in the sense that it showed all of the options. But the options were not in named groups, and so far, I haven't figured out how to do so - if it's even possible.
我发现了几个问题,答案是创建一个表单构造函数并在那里进行任何特殊处理".但似乎 forms.ChoiceField 需要一个 tuple 用于命名组,我不确定如何将元组转换为 QuerySet (如果我正确理解 QuerySets 的话,这可能是不可能的指向数据的指针,而不是实际数据).
I have found several questions, where the answer was, "create a form constructor and do any special processing there". But It seems like the forms.ChoiceField requires a tuple for named groups, and I’m not sure how to convert a tuple to a QuerySet (which is probably impossible anyway, if I understand QuerySets correctly as being pointer to the data, not the actual data).
我用于数据模型的代码是:
The code I used for the data Model is:
class ItemDesc(models.Model):
''' one of "food", "drink", where ID of "food" = 1, "drink" = 2 '''
desc = models.CharField(max_length=10, unique=True)
def __unicode__(self):
return self.desc
class MenuItem(models.Model):
''' one of ("burger", 1), ("pizza", 1), ("taco", 1),
("coke", 2), ("pepsi", 2), ("root beer", 2) '''
name = models.CharField(max_length=50, unique=True)
itemDesc = models.ForeignKey(ItemDesc)
def __unicode__(self):
return self.name
class PatronOrder(models.Model):
itemWanted = models.ForeignKey(MenuItem)
class ListMenuOrderForm(forms.ModelForm):
class Meta:
model = PatronOrder
def ListMenuOrder(request):
theForm = ListMenuOrderForm()
return render_to_response(menu_template, {'form': theForm,})
# generates the widget in right-side picture
如果需要,我将更改数据模型,但这似乎是一个简单的结构.也许太多的外键?折叠数据并接受非规范化?:) 或者有什么方法可以将元组转换为 QuerySet 或 ModelChoiceField 可以接受的东西?
I'll change the data model, if need be, but this seemed like a straightforward structure. Maybe too many ForeignKeys? Collapse the data and accept denormalization? :) Or is there some way to convert a tuple to a QuerySet, or something acceptable to a ModelChoiceField?
更新:最终代码,基于 meshantz' 回答:
Update: final code, based on meshantz' answer:
class FooIterator(forms.models.ModelChoiceIterator):
def __init__(self, *args, **kwargs):
super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs)
def __iter__(self):
yield ('food', [(1L, u'burger'), (2L, u'pizza')])
yield ('drinks', [(3L, u'coke'), (4L, u'pepsi')])
class ListMenuOrderForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ListMenuOrderForm, self).__init__(*args, **kwargs)
self.fields['itemWanted'].choices = FooIterator()
class Meta:
model = PatronOrder
(当然是实际代码,我会从数据库中拉取项目数据.)
(Of course the actual code, I'll have something pull the item data from the database.)
与他链接的 djangosnippet 相比,最大的变化似乎是 Django 合并了一些代码,从而可以直接将 Iterator 分配给 choices,而不必重写整个类.这是非常好的.
The biggest change from the djangosnippet he linked, appears to be that Django has incorporated some of the code, making it possible to directly assign an Iterator to choices, rather than having to override the entire class. Which is very nice.
解决方案
快速查看 django.forms.models 中的 ModelChoiceField 代码后,我会说尝试扩展该类并覆盖其选择属性.
After a quick look at the ModelChoiceField code in django.forms.models, I'd say try extending that class and override its choice property.
设置属性以返回自定义迭代器,基于同一模块中的原始 ModelChoiceIterator(返回您遇到问题的元组) - 新的 GroupedModelChoiceIterator 或类似的.
Set up the property to return a custom iterator, based on the orignial ModelChoiceIterator in the same module (which returns the tuple you're having trouble with) - a new GroupedModelChoiceIterator or some such.
我将不得不弄清楚如何将迭代器写给你,但我猜你只需要让它以自定义方式返回一个元组,而不是默认设置.
I'm going to have to leave the figuring out of exactly how to write that iterator to you, but my guess is you just need to get it returning a tuple of tuples in a custom manner, instead of the default setup.
很高兴回复评论,因为我很确定这个答案需要微调:)
Happy to reply to comments, as I'm pretty sure this answer needs a little fine tuning :)
在下面编辑
刚刚想到并检查了 djangosnippets,结果有人这样做了:ModelChoiceField with optiongroups.它已经有一年的历史了,所以它可能需要一些调整才能与最新的 django 一起使用,但这正是我的想法.
Just had a thought and checked djangosnippets, turns out someone's done just this: ModelChoiceField with optiongroups. It's a year old, so it might need some tweaks to work with the latest django, but it's exactly what I was thinking.
相关文章