39 votes

Clés étrangères dans l'affichage de la liste d'administration de django

Si un modèle django contient un champ de clé étrangère, et si ce champ est affiché en mode liste, il apparaît sous la forme suivante texte au lieu d'afficher un enlace à l'objet étranger.

Est-il possible d'afficher automatiquement toutes les clés étrangères sous forme de liens au lieu de texte plat ?

(bien sûr, il est possible de le faire champ par champ, mais existe-t-il une méthode générale).

Exemple :

class Author(models.Model):
    ...

class Post(models.Model):
    author = models.ForeignKey(Author)

Je choisis maintenant un ModelAdmin de telle sorte que l'auteur apparaisse en mode liste :

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

Maintenant, en mode liste, le champ de l'auteur utilisera simplement l'attribut __unicode__ de la méthode Author pour afficher l'auteur. En plus de cela, je voudrais un enlace pointant vers l'url de l'auteur correspondant dans le site d'administration. Est-ce possible ?

Méthode manuelle :

Par souci d'exhaustivité, j'ajoute la méthode manuelle. Il s'agirait d'ajouter une méthode author_link dans le PostAdmin classe :

def author_link(self, item):
    return '<a href="../some/path/%d">%s</a>' % (item.id, unicode(item))
author_link.allow_tags = True

Cela fonctionnera pour ce champ particulier mais c'est no ce que je veux. Je veux une méthode générale pour obtenir le même effet. (L'un des problèmes est de savoir comment déterminer automatiquement le chemin d'accès à un objet dans le site d'administration de django).

18voto

Itai Tavor Points 129

Je cherchais une solution au même problème et je suis tombé sur cette question... j'ai fini par la résoudre moi-même. L'OP n'est peut-être plus intéressé mais cela peut toujours être utile à quelqu'un.

from functools import partial
from django.forms import MediaDefiningClass

class ModelAdminWithForeignKeyLinksMetaclass(MediaDefiningClass):

    def __getattr__(cls, name):

        def foreign_key_link(instance, field):
            target = getattr(instance, field)
            return u'<a href="../../%s/%s/%d">%s</a>' % (
                target._meta.app_label, target._meta.module_name, target.id, unicode(target))

        if name[:8] == 'link_to_':
            method = partial(foreign_key_link, field=name[8:])
            method.__name__ = name[8:]
            method.allow_tags = True
            setattr(cls, name, method)
            return getattr(cls, name)
        raise AttributeError

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

class BookAdmin(admin.ModelAdmin):
    __metaclass__ = ModelAdminWithForeignKeyLinksMetaclass

    list_display = ('title', 'link_to_author')

Remplacez 'partial' par le 'curry' de Django si vous n'utilisez pas python >= 2.5.

1 votes

Itai Tavor, merci pour votre réponse, c'était vraiment utile, mais cela peut causer des problèmes, les détails sont en ma réponse à une autre question .

0 votes

Pour moi, l'utilisation du chemin absolu de <a href="http://stackoverflow.com/admin/%s/%s/%d">%s</a> fonctionnait mieux, car le lien était correct lorsqu'on se trouvait sur la page de détail de l'objet, plutôt que seulement sur la page de la liste.

0 votes

Quel est le paramètre de la méthode ModelAdminWith... ? Je n'ai pas de "MediaDefineClass" comme formulaire ?

10voto

celopes Points 4092

Je ne pense pas qu'il existe un mécanisme permettant de faire ce que vous voulez automatiquement dès le départ.

Mais pour ce qui est de déterminer le chemin d'accès à une page d'édition de l'administrateur à partir de l'identifiant d'un objet, il suffit de deux informations :

a) self.model._meta.app_label

b) self.model._meta.module_name

Ensuite, par exemple, pour aller à la page d'édition de ce modèle, vous feriez :

'../%s_%s_change/%d' % (self.model._meta.app_label, self.model._meta.module_name, item.id)

Jetez un coup d'œil à django.contrib.admin.options.ModelAdmin.get_urls pour voir comment ils s'y prennent.

Je suppose que vous pourriez avoir un appelable qui prend un nom de modèle et un id, crée un modèle du type spécifié juste pour obtenir l'étiquette et le nom (pas besoin de frapper la base de données) et génère l'URL ci-dessus.

Mais êtes-vous sûr que vous ne pouvez pas vous en sortir en utilisant des inlines ? L'interface utilisateur serait plus conviviale si tous les composants connexes se trouvaient sur une seule page...

Edit :

Inlines (lien vers la documentation) permettent à une interface d'administration d'afficher une relation parent-enfant sur une seule page au lieu de la diviser en deux.

Dans l'exemple de l'article et de l'auteur que vous avez fourni, l'utilisation d'inlines signifierait que la page de modification des articles afficherait également un formulaire inline pour ajouter/modifier/supprimer des auteurs. C'est beaucoup plus naturel pour l'utilisateur final.

Ce que vous pouvez faire dans votre vue de liste d'administration est de créer un appelable dans le modèle de poste qui rendra une liste d'auteurs séparés par des virgules. Ainsi, la liste des articles affichera les bons auteurs, et vous pourrez modifier les auteurs associés à un article directement dans l'interface d'administration de l'article.

0 votes

Merci pour cette réponse ! Pourriez-vous nous en dire plus sur l'utilisation des inlines ? En quoi cela peut-il être utile ?

0 votes

@Olivier : J'ai modifié ma réponse pour vous mettre dans la bonne direction concernant les inlines.

5voto

adolgarev Points 68

Voir https://docs.djangoproject.com/en/stable/ref/contrib/admin/#admin-reverse-urls

Exemple :

from django.utils.html import format_html
def get_admin_change_link(app_label, model_name, obj_id, name):
    url = reverse('admin:%s_%s_change' % (app_label, model_name),
              args=(obj_id,))
    return format_html('<a href="%s">%s</a>' % (
        url, unicode(name)
    ))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X