Can & # 8220; list_display & # 8221; dans un Django ModelAdmin les attributs d'affichage des champs ForeignKey?

StackOverflow https://stackoverflow.com/questions/163823

Question

J'ai un modèle Personne qui a une relation de clé étrangère avec Livre , qui comporte plusieurs champs, mais auteur (un CharField standard).

Cela dit, dans mon modèle PersonAdmin , j'aimerais afficher book.author à l'aide de list_display :

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

J'ai essayé toutes les méthodes évidentes pour le faire, mais rien ne semble fonctionner.

Des suggestions?

Était-ce utile?

La solution

Comme autre option, vous pouvez effectuer des recherches telles que:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

Autres conseils

En dépit de toutes les bonnes réponses ci-dessus et du fait que je sois nouveau à Django, j’étais toujours bloqué. Voici mon explication d'un point de vue très novice.

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py (méthode incorrecte) : vous pensez que cela fonctionnerait en utilisant "model__field" pour faire référence, mais pas

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py (méthode correcte) : c’est ainsi que vous faites référence à une clé étrangère nommée de la manière Django

.
class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

Pour plus de détails, consultez le lien du modèle Django ici

Comme le reste, j’y suis allé avec des callables. Mais ils ont un inconvénient: par défaut, vous ne pouvez pas commander sur eux. Heureusement, il existe une solution pour cela:

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

Notez que l'ajout de la fonction get_author ralentirait l'affichage de la liste (list_display) dans l'administrateur, car le fait d'afficher chaque personne effectuerait une requête SQL.

Pour éviter cela, vous devez modifier la méthode get_queryset dans PersonAdmin, par exemple:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')
  

Avant: 73 requêtes en 36.02ms (67 requêtes dupliquées dans l'administrateur)

     

Après: 6 requêtes en 10.81ms

Selon la documentation, vous ne pouvez afficher que la représentation __ unicode __ d'une clé étrangère:

http://docs.djangoproject.com/en / dev / ref / contrib / admin / # list-display

Cela semble étrange de ne pas prendre en charge le format de style 'book__author' qui est utilisé partout ailleurs dans l'API de base de données.

Il s’avère qu’il existe un ticket pour cette fonctionnalité , marqué comme ne sera pas réparé.

Vous pouvez afficher tout ce que vous voulez dans l'affichage de la liste en utilisant un appelable. Cela ressemblerait à ceci:

def book_author(object):
  return object.book.author

class PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

Je viens de publier un extrait qui rend la syntaxe '__' de support de admin.ModelAdmin:

http://djangosnippets.org/snippets/2887/

Vous pouvez donc faire:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

Il s’agit en gros de la même chose que celle décrite dans les autres réponses, mais elle prend automatiquement en charge (1) le paramétrage de admin_order_field (2) le paramétrage de short_description et (3) la modification du queryset pour éviter une frappe de base de données à chaque ligne.

Celui-ci a déjà été accepté, mais s'il existe d'autres modèles nuls (comme moi) qui ne l'ont pas immédiatement reçu du réponse actuellement acceptée , voici un peu plus de détails.

La classe de modèle référencée par ForeignKey doit contenir une méthode __ unicode __ , comme ici:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

Cela a fait la différence pour moi et devrait s’appliquer au scénario ci-dessus. Cela fonctionne sur Django 1.0.2.

Si vous l'essayez en ligne, vous n'y arriverez que si:

dans votre inline:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

dans votre modèle (MyModel):

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

Si vous avez beaucoup de champs d'attributs de relations à utiliser dans list_display et que vous ne voulez pas créer de fonction (ni d'attributs) pour chacun d'entre eux, une solution simple mais simple remplacera le ModelAdmin méthode instace __ getattr __ , créant les callables à la volée:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

En tant que l'essentiel ici

Les attributs spéciaux appelables tels que boolean et short_description doivent être définis comme attributs ModelAdmin , par exemple book__author_verbose_name = 'Nom de l'auteur' et category__is_new_boolean = True .

L'attribut admin_order_field appelable est défini automatiquement.

N'oubliez pas d'utiliser attribut list_select_related dans votre ModelAdmin pour que Django évite les requêtes supplémentaires.

PyPI contient un paquet très facile à utiliser qui gère exactement cela: django- related-admin . Vous pouvez également voir le code dans GitHub .

En utilisant ceci, ce que vous voulez réaliser est aussi simple que:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

Les deux liens contiennent des informations complètes sur l'installation et l'utilisation. Par conséquent, je ne les collerai pas ici au cas où ils changeraient.

Comme note de côté, si vous utilisez déjà autre chose que model.Admin (par exemple, j’utilisais plutôt SimpleHistoryAdmin ), vous pouvez procéder ainsi: < code> classe MyAdmin (SimpleHistoryAdmin, RelatedFieldAdmin) .

La réponse d'AlexRobbins a fonctionné pour moi, sauf que les deux premières lignes doivent figurer dans le modèle (c'est peut-être ce qui a été supposé?), et doivent faire référence à self:

def book_author(self):
  return self.book.author

Ensuite, la partie admin fonctionne correctement.

Je préfère ceci:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top