Opened 17 years ago
Closed 17 years ago
#4620 closed (fixed)
Custom labels for choices in ModelChoiceField
Reported by: | Owned by: | Jacob | |
---|---|---|---|
Component: | Forms | Version: | dev |
Severity: | Keywords: | ||
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I wanted to display a different value than the str() for a certain ModelChoiceField I was building. I added a func arg to both ModelChoiceField and QuerySetIterator to allow passing a function to change the behavior of the labels - e.g.,
field = forms.ModelChoiceField(IESGLogin.objects.all(), func = lambda ad: "%s, %s" % (ad.last_name, ad.first_name))
to get "Fenner, Bill" instead of the default str representation of "Bill Fenner".
ModelMultipleChoiceField is similarly modified, and you can of course call more complex functions
field2 = ModelMultipleChoiceField(IDState.objects.all(), func=lambda f:truncate_words(str(f),2))
Attachments (2)
Change History (17)
by , 17 years ago
Attachment: | queryset-lambda.diff added |
---|
comment:1 by , 17 years ago
Needs documentation: | set |
---|---|
Needs tests: | set |
Triage Stage: | Unreviewed → Design decision needed |
Looks good! I think perhaps func
should a bit more definitive, something like label_func
.
I'll leave as design decision for the big cheeses to review.
comment:2 by , 17 years ago
Bill submitted this ticket after talking with me on IRC. It's a nice addition, preventing my SELECT boxes from spanning 2 screen widths.
comment:3 by , 17 years ago
This would be an excellent feature to have. I was looking for a feature exactly like this until I found this patch.
comment:4 by , 17 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
The right way to do this is to subclass ModelChoiceField
(i.e. write a PersonChoiceField
) and add the correct behavior there.
comment:5 by , 17 years ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
It's a lot of hoops to jump through to do that however:
- Subclass
QuerySetIterator
- On your iterator subclass, override (and duplicate most of) the
__iter__
method to just yield something other thansmart_unicode(obj)
- Subclass
ModelChoiceField
orModelMultipleChoiceField
- On your field subclass, create (mostly duplicate)
_get_choices
and_set_choices
methods and setchoices = property(_get_choices, _set_choices)
so you can use your subclassed iterator
I've had this request again recently on IRC - are you sure it's wontfix? (even if it is in a different way to the current patch)
follow-up: 8 comment:6 by , 17 years ago
Resolution: | → wontfix |
---|---|
Status: | reopened → closed |
Yes, we're sure this is wontfix.
comment:7 by , 17 years ago
There are two possible solutions to this issue that don't involve passing a display function to the ModelChoiceField
:
Solution #1:
- Write a new method like
__unicode__
that returns whatever you need. - Subclass from
QuerySet
, overrideget
to replace the__unicode__
method of the returned objects. This subclass could be returned by a metaclass which receives the replacement__unicode__
function (or any other override, for that matter) in kwargs. - Subclass from
Manager
, overrideget_query_set
to return theQuerySet
subclass defined above. This subclass could also be returned by a metaclass which uses the above metaclass to createQuerySet
subclasses with specific parameters. - Make it possible to pass the custom manager to ModelChoiceField. Currently get_choices always uses _default_manager.
Solution #2:
- Write a new method like
__unicode__
that returns whatever you need. - Write a method for your model that clones the model, changes the
__unicode__
method to the new one, and returns the modified model instance. For the sake of this example let's call this methodshorter_display
. (This can work for any overrides.) - Make it possible to pass a custom choice list to
ModelChoiceField
. The elements of that list should be instances of the given model class. This parameter (let's call itchoices
) is mutually exclusive withlimit_choices_to
. field = ModelChoiceField(Model, choices=[object.shorter_display() for object in Model.objects.all()])
Both of the proposed solutions are considerably more flexible than the label_func
proposition. As overriding the label is a common scenario, the metaclasses in Solution #1 could actually be included in Django and not left to the users to create.
For the moment you can pass neither choices
nor manager
(and certainly not label_func
above) - in this situation I'm using a subclassed widget that specifies the choices on __init__
and render
. And I feel that this is a very ugly solution.
comment:8 by , 17 years ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
Replying to jacob:
Yes, we're sure this is wontfix.
Could you please give a short rationale on this given that having custom labels is not trivial as you did think before. The solution, as SmileyChris explained, involves some code duplication (maybe more code duplicated than the actual bits used for getting the custom label itself)
I have a class Foo
that have a user = ForeignKey(User)
and would like to choose users for Foo
instances by their full name instead of by their username. User
here is the standard class from django.contrib.auth
and so it's outside my control.
The needing of displaying full names instead of usernames in a ModelChoiceField
for django.contrib.auth.models.User
doesn't seem so uncommon to me.
comment:9 by , 17 years ago
Resolution: | → wontfix |
---|---|
Status: | reopened → closed |
Please bring such questions up on the django-developers list, rather than reopening the ticket (as documented in contributing.txt).
comment:10 by , 17 years ago
Patch needs improvement: | set |
---|---|
Resolution: | wontfix |
Status: | closed → reopened |
Triage Stage: | Design decision needed → Accepted |
Reopening after discussion on django-dev. I'm none to happy with the specific approach presented in this patch, but it *is* simply too hard to get custom labels out of a ModelChoiceField.
comment:11 by , 17 years ago
In the current patch, instead of having the default function be lambda obj: str(obj), why not just give it smart_unicode?
comment:12 by , 17 years ago
Owner: | changed from | to
---|---|
Status: | reopened → new |
Triage Stage: | Accepted → Ready for checkin |
comment:13 by , 17 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:14 by , 17 years ago
Needs documentation: | unset |
---|---|
Resolution: | fixed |
Status: | closed → reopened |
Triage Stage: | Ready for checkin → Accepted |
Version: | 0.96 → SVN |
comment:15 by , 17 years ago
Needs tests: | unset |
---|---|
Patch needs improvement: | unset |
Resolution: | → fixed |
Status: | reopened → closed |
was not used correctly, user reports it works as expected when used as documented.
Adds a function to the query set iterator and model*choicefield