Change Django ModelChoiceField to show users' full names rather than usernames

I have a form in my Django app (not in admin) that allows staff members to select a user from a dropdown.

forms.ModelChoiceField(queryset = User.objects.filter(is_staff=False), required = False)

The problem is that the dropdown shows users by usernames whereas I'd rather it show their full name from user.get_full_name() and use username only if that is not available. I only really need this change on this page, in other places like admin, I don't care if it uses username.

Is there a way I can do this?

Thanks!


Solution 1:

You can setup a custom ModelChoiceField that will return whatever label you'd like.

Place something like this within a fields.py or wherever applicable.

class UserModelChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
         return obj.get_full_name()

Then when creating your form, simply use that field

 UserModelChoiceField(queryset=User.objects.filter(is_staff=False), required = False)

More info can be found here

Solution 2:

When working with a ModelForm, I found the following most useful so that I didn't have to redefine my queryset - in particular because I used limit_choices_to in the model definition:

class MyModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyModelForm, self).__init__(*args, **kwargs)
        self.fields['user'].label_from_instance = lambda obj: "%s" % obj.get_full_name()

customised from this answer https://stackoverflow.com/a/7805824/432992

Solution 3:

You can override the field with a custom ModelChoiceField and change the label_from_instance function to return get_full_name instead. See the docs for ModelChoiceField: http://docs.djangoproject.com/en/1.2/ref/forms/fields/#modelchoicefield

Solution 4:

If you want to change choices of the field in model form, try this adaptation of the Bartek's answer:

model:

class MyModel(models.Model)
    user = models.ForeignKey(...)

form field:

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

form:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['user']
        field_classes = {
            'user': UserModelChoiceField
        }

This approach will preserve params of the field (you don't need to specify queryset, required and so on).