简体   繁体   中英

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

I have a few models having User as Foreign Key. The User list is displaying the username, but I'd like to customize it. Do I have to extend the User model with a custom model and write my own __str__ function? Is there an easier way? I don't think you can use a callable for fieldset, right?

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)

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

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

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'

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM