[Django] #27331: Proposed opt_group argument for ModelChoiceField and ModelMultipleChoiceField

43 views
Skip to first unread message

Django

unread,
Oct 9, 2016, 10:09:17 PM10/9/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------+---------------------------------------
Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Keywords: ModelChoiceField optgroup
Triage Stage: Unreviewed | Has patch: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------+---------------------------------------
Hello,

I've just implemented this and I thought It could well be incorporated
into Django itself; I guess it's a fairly common feature that one may need
on any project.

What I propose is to add a `opt_group` argument to ModelChoiceField and
ModelMultipleChoiceField; which indicates the item's field whose value is
used to group the choices. It should be used in conjunction with a
queryset which is (primarily) sorted by the same field.

Let me show with an example:
{{{
#!python
class Category(models.Model):
name = models.CharField(max_length=20)

class Item(models.Model):
name = models.CharField(max_length=20)
category = models.ForeignKey(Category)
}}}
And in some form's initialization process
{{{
#!python
field = ModelChoiceField(queryset=Item.objects.order_by('category__name',
'name'), opt_group='category')
}}}
field.choices will dynamically collect choices into named groups as a
2-tuple, which the underlying widget should present using an optgroup HTML
element.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Oct 9, 2016, 11:15:28 PM10/9/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Héctor Urbina):

* needs_docs: => 0
* needs_tests: => 0
* needs_better_patch: => 0


Old description:

> Hello,
>
> I've just implemented this and I thought It could well be incorporated
> into Django itself; I guess it's a fairly common feature that one may
> need on any project.
>
> What I propose is to add a `opt_group` argument to ModelChoiceField and
> ModelMultipleChoiceField; which indicates the item's field whose value is
> used to group the choices. It should be used in conjunction with a
> queryset which is (primarily) sorted by the same field.
>
> Let me show with an example:
> {{{
> #!python
> class Category(models.Model):
> name = models.CharField(max_length=20)
>
> class Item(models.Model):
> name = models.CharField(max_length=20)
> category = models.ForeignKey(Category)
> }}}
> And in some form's initialization process
> {{{
> #!python
> field = ModelChoiceField(queryset=Item.objects.order_by('category__name',
> 'name'), opt_group='category')
> }}}
> field.choices will dynamically collect choices into named groups as a
> 2-tuple, which the underlying widget should present using an optgroup
> HTML element.

New description:

Hello,

I've just implemented this and I thought It could well be incorporated
into Django itself; I guess it's a fairly common feature that one may need
on any project.

What I propose is to add a `opt_group` argument to ModelChoiceField and
ModelMultipleChoiceField; which indicates the item's field whose value is
used to group the choices. It should be used in conjunction with a
queryset which is (primarily) sorted by the same field.

Let me show with an example:
{{{
#!python
class Category(models.Model):
name = models.CharField(max_length=20)

class Item(models.Model):
name = models.CharField(max_length=20)
category = models.ForeignKey(Category)
}}}
And in some form's initialization process
{{{
#!python
field = ModelChoiceField(queryset=Item.objects.order_by('category__name',
'name'), opt_group='category')
}}}
`field.choices` will dynamically collect choices into named groups as a

2-tuple, which the underlying widget should present using `optgroup` HTML
elements.

--

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:1>

Django

unread,
Oct 10, 2016, 9:22:57 AM10/10/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham):

* has_patch: 1 => 0


Comment:

At first glance, that looks more appropriate as a widget option since
`ModelChoiceField` isn't required to use a `Select` widget.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:2>

Django

unread,
Oct 10, 2016, 12:05:45 PM10/10/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Héctor Urbina):

Replying to [comment:2 Tim Graham]:


> At first glance, that looks more appropriate as a widget option since
`ModelChoiceField` isn't required to use a `Select` widget.

The default `ModelChoiceField`'s widget (`Select`) is prepared to
understand 2-tuples coming on the field's choices attribute and translates
them to optgroups.
`ModelChoiceField` uses `ModelChoiceIterator` to generate the choices;
that is the actual class that is doing the job on
[https://github.com/ajendrex/django/commit/1ae142489f36d65b5cd0a51c243040c571fe01b8
my current implementation]. My idea is to include support for optgroup,
not require it; I'm updating my proposal to clarify that.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:3>

Django

unread,
Oct 10, 2016, 12:06:12 PM10/10/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Héctor Urbina:

Old description:

> Hello,
>
> I've just implemented this and I thought It could well be incorporated
> into Django itself; I guess it's a fairly common feature that one may
> need on any project.
>
> What I propose is to add a `opt_group` argument to ModelChoiceField and
> ModelMultipleChoiceField; which indicates the item's field whose value is
> used to group the choices. It should be used in conjunction with a
> queryset which is (primarily) sorted by the same field.
>
> Let me show with an example:
> {{{
> #!python
> class Category(models.Model):
> name = models.CharField(max_length=20)
>
> class Item(models.Model):
> name = models.CharField(max_length=20)
> category = models.ForeignKey(Category)
> }}}
> And in some form's initialization process
> {{{
> #!python
> field = ModelChoiceField(queryset=Item.objects.order_by('category__name',
> 'name'), opt_group='category')
> }}}
> `field.choices` will dynamically collect choices into named groups as a

> 2-tuple, which the underlying widget should present using `optgroup` HTML
> elements.

New description:

Hello,

I've just implemented this and I thought It could well be incorporated
into Django itself; I guess it's a fairly common feature that one may need
on any project.

What I propose is to add an optional `opt_group` argument to


ModelChoiceField and ModelMultipleChoiceField; which indicates the item's
field whose value is used to group the choices. It should be used in
conjunction with a queryset which is (primarily) sorted by the same field.

Let me show with an example:
{{{
#!python
class Category(models.Model):
name = models.CharField(max_length=20)

class Item(models.Model):
name = models.CharField(max_length=20)
category = models.ForeignKey(Category)
}}}
And in some form's initialization process
{{{
#!python
field = ModelChoiceField(queryset=Item.objects.order_by('category__name',
'name'), opt_group='category')
}}}
`field.choices` will dynamically collect choices into named groups as a

2-tuple, which the underlying widget should present using `optgroup` HTML
elements.

--

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:4>

Django

unread,
Oct 10, 2016, 12:41:14 PM10/10/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham):

I'm still unconvinced that adding an argument with an HTML looking name to
a model field that only affects selected widgets is a good design. I see
that it's convenient compared to the alternative of providing a custom
`ModelChoiceIterator` but I don't know that the coupling is a good design.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:5>

Django

unread,
Oct 12, 2016, 3:50:43 PM10/12/16
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: master
Severity: Normal | Resolution: wontfix

Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham):

* status: new => closed
* resolution: => wontfix


Comment:

As the discussion has stalled here, you can write to the
DevelopersMailingList for other opinions. Thanks.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:6>

Django

unread,
Dec 20, 2018, 7:21:39 AM12/20/18
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: master

Severity: Normal | Resolution: wontfix
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Simon Charette):

By the way, this is currently possible with the following code

{{{#!python
from functools import partial
from itertools import groupby
from operator import attrgetter

class GroupedModelChoiceIterator(ModelChoiceIterator):
def __init__(self, field, groupby):
self.groupby = groupby
super(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)
self.iterator = partial(GroupedModelChoiceIterator,
groupby=choices_groupby)
super(*args, **kwargs)
}}}

While I won't push to get this feature included I think that a request for
feedback on django-developers has a good chance of being accepted. It
looks like the main argument against closing this ticket as _wontfix_ was
the naming of the `ModeChoice(opt_group)` argument.

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:7>

Django

unread,
Jul 6, 2020, 11:44:49 AM7/6/20
to django-...@googlegroups.com
#27331: Proposed opt_group argument for ModelChoiceField and
ModelMultipleChoiceField
-------------------------------------+-------------------------------------

Reporter: Héctor Urbina | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: master

Severity: Normal | Resolution: wontfix
Keywords: ModelChoiceField | Triage Stage:
optgroup | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by angelafoli):

Replying to [comment:7 Simon Charette]:


> By the way, this is currently possible with the following code
>
> {{{#!python
> from functools import partial
> from itertools import groupby
> from operator import attrgetter
>
> 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)


> }}}
>
> While I won't push to get this feature included I think that a request
for feedback on django-developers has a good chance of being accepted. It
looks like the main argument against closing this ticket as _wontfix_ was

the naming of the `ModelChoice(opt_group)` argument which was effectively
leaking HTML implementation details at the form layer.

This solution no longer works as of this change:
https://github.com/django/django/commit/5ec64f96b2d83ec3c0ef574f52e4767a440017b8

--
Ticket URL: <https://code.djangoproject.com/ticket/27331#comment:8>

Reply all
Reply to author
Forward
0 new messages