Django

Code

Ticket #4620 (closed: fixed)

Opened 1 year ago

Last modified 4 months ago

Custom labels for choices in ModelChoiceField

Reported by: Bill Fenner <fenner@gmail.com> Assigned to: jacob
Milestone: Component: django.newforms
Version: SVN Keywords:
Cc: Triage Stage: Accepted
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

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

queryset-lambda.diff (2.9 kB) - added by Bill Fenner <fenner@gmail.com> on 06/18/07 23:19:59.
Adds a function to the query set iterator and model*choicefield
4620_all.diff (4.8 kB) - added by PhiR on 03/19/08 10:27:01.
patch with fix + doc + tests

Change History

06/18/07 23:19:59 changed by Bill Fenner <fenner@gmail.com>

  • attachment queryset-lambda.diff added.

Adds a function to the query set iterator and model*choicefield

06/18/07 23:24:47 changed by SmileyChris

  • needs_better_patch changed.
  • stage changed from Unreviewed to Design decision needed.
  • needs_tests set to 1.
  • needs_docs set to 1.

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.

06/19/07 08:59:19 changed by david@dawninglight.net

Bill submitted this ticket after talking with me on IRC. It's a nice addition, preventing my SELECT boxes from spanning 2 screen widths.

09/22/07 22:55:32 changed by bsdlogical

This would be an excellent feature to have. I was looking for a feature exactly like this until I found this patch.

11/30/07 15:06:05 changed by jacob

  • status changed from new to closed.
  • resolution set to wontfix.

The right way to do this is to subclass ModelChoiceField (i.e. write a PersonChoiceField) and add the correct behavior there.

11/30/07 15:19:33 changed by SmileyChris

  • status changed from closed to reopened.
  • resolution deleted.

It's a lot of hoops to jump through to do that however:

1. Subclass QuerySetIterator

2. On your iterator subclass, override (and duplicate most of) the __iter__ method to just yield something other than smart_unicode(obj)

3. Subclass ModelChoiceField or ModelMultipleChoiceField

4. On your field subclass, create (mostly duplicate) _get_choices and _set_choices methods and set choices = 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 ) 11/30/07 15:24:42 changed by jacob

  • status changed from reopened to closed.
  • resolution set to wontfix.

Yes, we're sure this is wontfix.

12/06/07 06:18:51 changed by sjulean

There are two possible solutions to this issue that don't involve passing a display function to the ModelChoiceField:

Solution #1:

  1. Write a new method like __unicode__ that returns whatever you need.
  2. Subclass from QuerySet, override get 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.
  3. Subclass from Manager, override get_query_set to return the QuerySet subclass defined above. This subclass could also be returned by a metaclass which uses the above metaclass to create QuerySet subclasses with specific parameters.
  4. Make it possible to pass the custom manager to ModelChoiceField?. Currently get_choices always uses _default_manager.

Solution #2:

  1. Write a new method like __unicode__ that returns whatever you need.
  2. 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 method shorter_display. (This can work for any overrides.)
  3. 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 it choices) is mutually exclusive with limit_choices_to.
  4. 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.

(in reply to: ↑ 6 ) 02/24/08 16:29:38 changed by moya

  • status changed from closed to reopened.
  • resolution deleted.

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.

02/24/08 16:33:11 changed by mtredinnick

  • status changed from reopened to closed.
  • resolution set to wontfix.

Please bring such questions up on the django-developers list, rather than reopening the ticket (as documented in contributing.txt).

02/29/08 10:12:53 changed by jacob

  • status changed from closed to reopened.
  • needs_better_patch set to 1.
  • resolution deleted.
  • stage changed from Design decision needed to 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.

03/03/08 15:29:13 changed by Alex

In the current patch, instead of having the default function be lambda obj: str(obj), why not just give it smart_unicode?

03/19/08 10:27:01 changed by PhiR

  • attachment 4620_all.diff added.

patch with fix + doc + tests

03/19/08 10:39:39 changed by jacob

  • owner changed from nobody to jacob.
  • status changed from reopened to new.
  • stage changed from Accepted to Ready for checkin.

03/19/08 18:10:45 changed by jacob

  • status changed from new to closed.
  • resolution set to fixed.

(In [7326]) Fixed #4620: you can now easily add custom labels to ModelChoiceFields? via subclassing. Thanks, PhiR.

03/20/08 10:54:24 changed by PhiR

  • status changed from closed to reopened.
  • needs_docs deleted.
  • version changed from 0.96 to SVN.
  • resolution deleted.
  • stage changed from Ready for checkin to Accepted.

03/21/08 09:45:12 changed by PhiR

  • status changed from reopened to closed.
  • needs_better_patch deleted.
  • resolution set to fixed.
  • needs_tests deleted.

was not used correctly, user reports it works as expected when used as documented.


Add/Change #4620 (Custom labels for choices in ModelChoiceField)




Change Properties
Action