如何对 Django Select 小部件中的选项进行分组?

问题描述 投票:0回答:4

当 Django 选择(下拉)小部件位于从数据模型“自动生成”的表单上时,是否可以在 Django 选择(下拉)小部件中创建“命名选择组”?我可以在下面的左侧图片中创建小部件吗?

我创建带有命名组的表单的第一个实验是

手动Two widgets with one grouped完成的,如下所示:

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 它工作得很好,在左侧创建了带有命名组的下拉小部件。

然后我创建了一个具有基本相同结构的数据模型,并使用 Django 从模型自动生成表单的功能。它起作用了——从某种意义上说,它显示了所有的选项。但这些选项并不在命名组中,到目前为止,我还没有弄清楚如何做到这一点 - 如果可能的话。

我发现了几个问题,答案是“创建一个表单构造函数并在那里进行任何特殊处理”。但似乎 forms.ChoiceField 需要一个

tuple

来表示命名组,并且我不确定如何将元组转换为 QuerySet (无论如何这可能是不可能的,如果我正确地将 QuerySets 理解为指向数据的指针,不是实际数据)。

我用于数据模型的代码是: 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

如果需要,我将更改数据模型,但这似乎是一个简单的结构。也许外键太多?折叠数据并接受非规范化? :) 或者有什么方法可以将元组转换为查询集,或者 ModelChoiceField 可以接受的

某些东西?

更新:最终代码,基于meshantz

'

答案 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 (当然,实际的代码,我会从数据库

中提取项目数据。)

他链接的 djangosnippet 的最大变化似乎是 Django 合并了一些代码,使得可以直接将迭代器分配给

choices,而不必重写整个类。这非常好。

快速查看 django.forms.models 中的 ModelChoiceField 代码后,我会说尝试扩展该类并覆盖其 choice 属性。

设置属性以返回自定义迭代器,基于同一模块中的原始 ModelChoiceIterator(它返回您遇到问题的元组) - 一个新的 GroupedModelChoiceIterator 或类似的东西。
python django widget
4个回答
14
投票
我将不得不不去思考如何向您编写该迭代器,但我的猜测是您只需要让它以自定义方式返回元组的元组,而不是默认设置。

很高兴回复评论,因为我很确定这个答案需要一些微调:)

编辑如下

刚刚想到并检查了 djangosnippets,结果发现有人这样做了:

带有选项组的 ModelChoiceField 。它已经有一年了,所以可能需要一些调整才能与最新的 django 一起使用,但这正是我的想法。

这对我有用,没有扩展任何当前的 django 类:

我有一个生物体类型列表,以不同的王国作为选择组。在 OrganismForm 表单中,您可以从下拉选择框中选择生物体,它们按王国的 optgroup 排序,然后是该王国的所有生物体。像这样:

6
投票
[----------------|V] |Plantae | | Angiosperm | | Conifer | |Animalia | | Mammal | | Amphibian | | Marsupial | |Fungi | | Zygomycota | | Ascomycota | | Basidiomycota | | Deuteromycota | |... | |________________|

模型.py

from django.models import Model class Kingdom(Model): name = models.CharField(max_length=16) class Organism(Model): kingdom = models.ForeignKeyField(Kingdom) name = models.CharField(max_length=64)

forms.py:

from models import Kingdom, Organism class OrganismForm(forms.ModelForm): organism = forms.ModelChoiceField( queryset=Organism.objects.all().order_by('kingdom__name', 'name') ) class Meta: model = Organism

views.py:

from models import Organism, Kingdom from forms import OrganismForm form = OrganismForm() form.fields['organism'].choices = list() # Now loop the kingdoms, to get all organisms in each. for k in Kingdom.objects.all(): # Append the tuple of OptGroup Name, Organism. form.fields['organism'].choices = form.fields['organism'].choices.append( ( k.name, # First tuple part is the optgroup name/label list( # Second tuple part is a list of tuples for each option. (o.id, o.name) for o in Organism.objects.filter(kingdom=k).order_by('name') # Each option itself is a tuple of id and name for the label. ) ) )
    

您不需要
自定义迭代器
。您需要支持

3
投票
。只需通过

正确选项即可: from django import forms from django.db.models import Prefetch class ProductForm(forms.ModelForm): class Meta: model = Product fields = [...] def __init__(self, *args, **kwargs): super(ProductForm, self).__init__(*args, **kwargs) cats = Category.objects \ .filter(category__isnull=True) \ .order_by('order') \ .prefetch_related(Prefetch('subcategories', queryset=Category.objects.order_by('order'))) self.fields['subcategory'].choices = \ [("", self.fields['subcategory'].empty_label)] \ + [(c.name, [ (self.fields['subcategory'].prepare_value(sc), self.fields['subcategory'].label_from_instance(sc)) for sc in c.subcategories.all() ]) for c in cats] 这里,

class Category(models.Model): category = models.ForeignKey('self', null=True, on_delete=models.CASCADE, related_name='subcategories', related_query_name='subcategory') class Product(models.Model): subcategory = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products', related_query_name='product')

同样的技术可用于自定义

Django 管理表单

。不过,在这种情况下不需要
Meta
类。

您必须将其作为嵌套元组传递:

MEDIA_CHOICES = ( ('Audio', ( ('vinyl', 'Vinyl'), ('cd', 'CD'), ) ), ('Video', ( ('vhs', 'VHS Tape'), ('dvd', 'DVD'), ) ), )

0
投票

https://stackoverflow.com/a/17854288/796437

    

© www.soinside.com 2019 - 2024. All rights reserved.