94 votes

Django Admin affiche une image à partir d'un champ d'images

Si je peux afficher une image téléchargée dans list_display, est-il possible de le faire sur la page par modèle (comme dans la page que vous obtenez pour changer un modèle) ?

Un exemple rapide de modèle serait :

Class Model1(models.Model):
    image = models.ImageField(upload_to=directory)

L'administration par défaut affiche l'url de l'image téléchargée mais pas l'image elle-même.

Gracias.

11voto

monotonee Points 121

Bien qu'il y ait quelques bonnes solutions fonctionnelles déjà partagées ici, je pense que le balisage non formel, comme les balises d'image auxiliaires, doit être placé dans les modèles, et non pas ajouté aux widgets de formulaire de Django ou généré dans les classes d'administration des modèles. Une solution plus sémantique est :

Remplacement du modèle d'administration

Note : Apparemment, ma réputation n'est pas assez élevée pour poster plus de deux simples liens, j'ai donc créé des annotations dans le texte suivant et inclus les URLs respectifs au bas de cette réponse.

De la Documentation sur le site d'administration de Django :

Il est relativement facile de remplacer un grand nombre des modèles que le module d'administration utilise pour générer les différentes pages d'un site d'administration. Vous pouvez même remplacer certains de ces modèles pour une application ou un modèle spécifique.

Le programme de Django django.contrib.admin.options.ModelAdmin (généralement accessible sous l'espace de noms django.contrib.admin.ModelAdmin ) présente une série de chemins de modèles possibles pour Le chargeur de modèles de Django dans l'ordre du plus spécifique au moins spécifique. Cet extrait a été copié directement de django.contrib.admin.options.ModelAdmin.render_change_form :

return TemplateResponse(request, form_template or [
    "admin/%s/%s/change_form.html" % (app_label, opts.model_name),
    "admin/%s/change_form.html" % app_label,
    "admin/change_form.html"
], context)

Par conséquent, compte tenu de la documentation susmentionnée sur le remplacement des modèles d'administration de Django et des chemins de recherche des modèles, supposons que l'on ait créé une application "articles" dans laquelle est définie une classe de modèle "Article". Si l'on veut remplacer ou étendre seulement le formulaire de modification du site d'administration par défaut de Django pour le modèle articles.models.Article il faut suivre les étapes suivantes :

  1. Créez une structure de répertoire modèle pour le fichier de remplacement.
    • Bien que la documentation ne le mentionne pas, le chargeur de modèles cherchera d'abord dans les répertoires d'applications si APP_DIRS 1 est réglé sur True .
    • Étant donné que l'on souhaite remplacer le modèle de site d'administration de Django par une étiquette d'application et par un modèle, la hiérarchie de répertoire résultante serait la suivante : <project_root>/articles/templates/admin/articles/article/
  2. Créez le(s) fichier(s) modèle(s) dans votre nouvelle structure de répertoire.
    • Seul le formulaire de modification de l'administrateur doit être modifié. change_form.html .
    • Le chemin final, absolu, sera <project_root>/articles/templates/admin/articles/article/change_form.html
  3. Remplacez complètement ou étendez simplement le modèle de formulaire de modification de l'administrateur par défaut.
    • Je n'ai pas pu trouver d'informations dans la documentation de Django concernant les données contextuelles disponibles pour les modèles de sites d'administration par défaut, j'ai donc été obligé de consulter le code source de Django.
      • Modèle de formulaire de modification par défaut : github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html
      • Quelques-unes des définitions pertinentes du dictionnaire contextuel se trouvent à l'adresse suivante django.contrib.admin.options.ModelAdmin._changeform_view y django.contrib.admin.options.ModelAdmin.render_change_form

Ma solution

En supposant que le nom de l'attribut ImageField du modèle est "file", le remplacement du modèle pour mettre en œuvre les aperçus d'images serait similaire à ceci :

{% extends "admin/change_form.html" %}

{% block field_sets %}
{% if original %}
<div class="aligned form-row">
    <div>
        <label>Preview:</label>
        <img
            alt="image preview"
            src="/{{ original.file.url }}"
            style="max-height: 300px;">
    </div>
</div>
{% endif %}
{% for fieldset in adminform %}
  {% include "admin/includes/fieldset.html" %}
{% endfor %}
{% endblock %}

original semble être l'instance de modèle à partir de laquelle le ModelForm a été généré. Pour l'anecdote, je n'utilise généralement pas de CSS en ligne, mais cela ne valait pas la peine de créer un fichier séparé pour une seule règle.

Sources :

  1. docs.djangoproject.com/fr/dev/ref/settings/#app-dirs

8voto

Dettlaff Points 142

J'ai essayé de le comprendre moi-même et voici ce que j'ai trouvé.

@admin.register(ToDo)
class ToDoAdmin(admin.ModelAdmin):
    def image_tag(self, obj):
        return format_html('<img src="{}" width="auto" height="200px" />'.format(obj.img.url))

    image_tag.short_description = 'Image'

    list_display = ['image_tag']
    readonly_fields = ['image_tag']

5voto

Alex Jolig Points 466

C'est comment cela fonctionne pour django 2.1 sans modification models.py :

Dans votre Hero modèle, vous avez un champ d'image. :

headshot = models.ImageField(null=True, blank=True, upload_to="hero_headshots/")

Vous pouvez le faire comme ça :

@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):

    readonly_fields = [..., "headshot_image"]

    def headshot_image(self, obj):
        return mark_safe('<img src="{url}" width="{width}" height={height} />'.format(
            url = obj.headshot.url,
            width=obj.headshot.width,
            height=obj.headshot.height,
            )
    )

3voto

chiwai Points 11

Mise à jour de Django 2.1 pour le projet de Venkat Kotra réponse . La réponse fonctionne bien sur Django 2.0.7 et inférieur. Mais donne une erreur de serveur 500 (si DEBUG=False ) ou donne

render() got an unexpected keyword argument 'renderer'

La raison en est que dans Django 2.1 : Support for Widget.render() methods without the renderer argument is removed. Donc, le paramètre renderer est maintenant obligatoire. Nous devons mettre à jour la fonction render() de AdminImageWidget pour inclure le paramètre renderer . Et ce doit être après attrs (avant kwargs si vous l'avez) :

class AdminImageWidget(AdminFileWidget):
    def render(self, name, value, attrs=None, renderer=None):
        output = []
        if value and getattr(value, "url", None):
            image_url = value.url
            file_name = str(value)
            output.append(u' <a href="%s" target="_blank"><img src="%s" alt="%s" /></a> %s ' % \
                      (image_url, image_url, file_name, _('Change:')))
        output.append(super(AdminFileWidget, self).render(name, value, attrs, renderer))
        return mark_safe(u''.join(output))

3voto

Gustavo Mex Points 31

Django ver. 3.0.3

models.py :

def image_tag(self):
    from django.utils.html import mark_safe
    return mark_safe('<img src="%s" width="100px" height="100px" />'%(self.image.url))
image_tag.short_description = 'Image'

admin.py :

list_display = ('image_tag', )

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