16

I have a model that references a ForeignKey(User) field.

When a user selects an item on his form I would like them to be able to see the get_full_name() instead of just the username.

class Books(models.Model):
     author = models.ForeignKey(User)
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
ApPeL
  • 4,801
  • 9
  • 47
  • 84
  • 4
    Always name your model class in singular, write `Book` instead of `Books`. – viam0Zah Oct 19 '10 at 09:10
  • possible duplicate of [Django forms: how to dynamically create ModelChoiceField labels](http://stackoverflow.com/questions/2958792/django-forms-how-to-dynamically-create-modelchoicefield-labels) – viam0Zah Oct 19 '10 at 09:18

2 Answers2

37

This can be done several ways.

Create a proxy subclass of User and override its __unicode__() method to return user's full name.

class UserFullName(User):
    class Meta:
        proxy = True

    def __unicode__(self):
        return self.get_full_name()

Now in your model form, use UserFullName to retrieve users.

class BookForm(forms.ModelForm):
    author = forms.ModelChoiceField(queryset=UserFullName.objects.all())
    class Meta:
        model = Book

Another way is to dynamically populate choices in form's constructor.

class BookForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(BookForm, self).__init__(*args, **kwargs)
        users = User.objects.all()
        self.fields['author'].choices = [(user.pk, user.get_full_name()) for user in users]

    class Meta:
        model = Book

Perhaps, the most "djangonic" way is demonstrated by lazerscience as an answer to the similar question Django forms: how to dynamically create ModelChoiceField labels. It subclasses ModelChoiceField and overrides its label_from_instance() method that is intended to provide choice labels.

class UserFullnameChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return smart_unicode(obj.get_full_name())

class BookForm(forms.ModelForm):
    author = UserFullnameChoiceField(queryset=User.objects.all())

    class Meta:
        model = Book
Community
  • 1
  • 1
viam0Zah
  • 25,949
  • 8
  • 77
  • 100
  • 2
    Thanks, the last option (most "djangonic" way) seems the cleanest and worked fine. Don't forget a `from django.utils.encoding import smart_unicode`. – SaeX Oct 05 '15 at 20:37
  • Thank you for this answer you help me solve the problem – Joseph Daudi Sep 06 '17 at 11:05
0

In addition to Török Gábor's answer, the following can be used for ModelMultipleChoiceFields:

class UserFullnameMultipleChoiceField(ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        return smart_unicode(obj.get_full_name())

class BookForm(forms.ModelForm):
    authors = UserFullnameMultipleChoiceField(queryset=User.objects.all(),
                                              help_text=Book.authors.field.help_text)

Note that I have copy help_text so the default help text ('Use control or command to select multiple...').

Bouke
  • 11,768
  • 7
  • 68
  • 102