[Django]-How to change User representation in Django Admin when used as Foreign Key?

16πŸ‘

βœ…

I think __unicode__() method is not the correct, you should use __str__() method.

For Python 2.x, __str__() method will return str(bytes) and __unicode__() method will return unicode (text).

The print statement and the str built-in call __str__() to determine
the human-readable representation of an object. The unicode built-in
calls __unicode__() if it exists, and otherwise falls back to
__str__() and decodes the result with the system encoding. Conversely, the Model base class automatically derives __str__() from
__unicode__() by encoding to UTF-8.
read here complete

But in Python 3.x there is just __str__(), no __unicode__() method.

Django provides a simple way to define __str__() and __unicode__()
methods that work on Python 2 and 3: you must define a __str__()
method returning text and to apply the python_2_unicode_compatible()
decorator.

On Python 3, the decorator is a no-op. On Python 2, it defines
appropriate __unicode__() and __str__() methods (replacing the
original __str__() method in the process).

Here is an example from django docs.

from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class MyClass(object):
    def __str__(self):
        return "Instance of my class"

SOLUTION : Decorate in the same way, as done above for your Class and
in models.py, add a method which will be get added to the User model.

from django.contrib.auth.models import User

def get_name(self):
    return '{} {}'.format(self.first_name, self.last_name)

User.add_to_class("__str__", get_name)
πŸ‘€Surajano

6πŸ‘

ForeignKeys in Django forms are represented by ModelChoiceFields.
The ModelChoiceField class has a label_from_instance method, which decides how to render the choice. So in order to change this representation in the Admin, you need to modify this form field.

Here is a simple example.

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        field = super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == "user":
            field.label_from_instance = lambda u: "My Object #%i" % u.id
        return field
πŸ‘€Todor

3πŸ‘

You can change the string representation of the Django default User by overriding the User __unicode__ method. Add the code below somewhere in your app, perhaps in models.py

def custom_user_display(self):
    return self.email + ', ' self.first_name # write your representation here

User.add_to_class("__unicode__", custom_user_display) # override the __unicode__ method
πŸ‘€jastr

3πŸ‘

Assume you have a model like this

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True, verbose_name=_('user'), related_name='profile')
    city = models.CharField(max_length=128, null=True, blank=True)

And you want to show the user’s email instead of username or userid in admin page, then you can do something like this

class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('get_user', 'city')

    def get_user(self, obj):
        return obj.user.email
    get_user.short_description = 'User'
    get_user.admin_order_field = 'user__id'
πŸ‘€Iqbal

0πŸ‘

I had the same problem and I had to extend @Todor β€˜s solution for many models and different apps.

In the end, it was better to override the default AdminSite and hook on register to customize the User form fields.


from django.contrib import admin
from django.contrib.admin import ModelAdmin
from django.contrib.auth import get_user_model

def user_admin_str(instance: get_user_model()) -> str:
    return f"{instance.last_name}-chan"


def formfield_for_foreignkey(self, db_field, request, **kwargs):
    field = super(ModelAdmin, self).formfield_for_foreignkey(
        db_field, request, **kwargs)
    if isinstance(db_field.related_model(), get_user_model()):
        field.label_from_instance = user_admin_str
    return field


def formfield_for_manytomany(self, db_field, request, **kwargs):
    field = super(ModelAdmin, self).formfield_for_manytomany(
        db_field, request, **kwargs)
    if isinstance(db_field.related_model(), get_user_model()):
        field.label_from_instance = user_admin_str
    return field


class CustomAdminSite(admin.AdminSite):

    def register(self, model_or_iterable, admin_class=None, **options):
        setattr(admin_class, "formfield_for_foreignkey",
                formfield_for_foreignkey)
        setattr(admin_class, "formfield_for_manytomany",
                formfield_for_manytomany)
        super().register(model_or_iterable, admin_class=admin_class, **options)

No need to modify every ModelAdmin then.

πŸ‘€dmcontador

Leave a comment