85

虽然我可以在 list_display 中显示上传的图像,但是否可以在每个模型页面上执行此操作(如在更改模型的页面中)?

一个快速的示例模型是:

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

默认管理员显示上传图片的 url,但不显示图片本身。

谢谢!

4

13 回答 13

135

Sure. In your model class add a method like:

def image_tag(self):
    from django.utils.html import escape
    return u'<img src="%s" />' % escape(<URL to the image>)
image_tag.short_description = 'Image'
image_tag.allow_tags = True

and in your admin.py add:

fields = ( 'image_tag', )
readonly_fields = ('image_tag',)

to your ModelAdmin. If you want to restrict the ability to edit the image field, be sure to add it to the exclude attribute.

Note: With Django 1.8 and 'image_tag' only in readonly_fields it did not display. With 'image_tag' only in fields, it gave an error of unknown field. You need it both in fields and in readonly_fields in order to display correctly.

于 2013-04-30T19:29:55.293 回答
96

除了 Michael C. O'Connor 的回答

请注意,自从 Django v.1.9(更新 - 测试并一直工作到 Django 3.0)

image_tag.allow_tags = True

已弃用,您应该改用 format_html()、format_html_join() 或 mark_safe()

因此,如果您将上传的文件存储在公共 /directory 文件夹中,您的代码应如下所示:

from django.utils.html import mark_safe


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

        def image_tag(self):
            return mark_safe('<img src="/directory/%s" width="150" height="150" />' % (self.image))

        image_tag.short_description = 'Image'

并在您的 admin.py 添加:

fields = ['image_tag']
readonly_fields = ['image_tag']
于 2016-06-22T10:16:57.123 回答
55

无需修改模型即可在管理员中完成

from django.utils.html import format_html

@admin.register(Model1) 
class Model1Admin(admin.ModelAdmin):

    def image_tag(self, obj):
        return format_html('<img src="{}" />'.format(obj.image.url))

    image_tag.short_description = 'Image'

    list_display = ['image_tag',]
于 2016-11-21T08:31:46.363 回答
19

对于 Django 1.9 要在编辑页面中显示图像而不是文件路径,使用 ImageWidget 是很好的方法。

from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
from django.contrib import admin


class AdminImageWidget(AdminFileWidget):
    def render(self, name, value, attrs=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))
        return mark_safe(u''.join(output))


class ImageWidgetAdmin(admin.ModelAdmin):
    image_fields = []

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in self.image_fields:
            request = kwargs.pop("request", None)
            kwargs['widget'] = AdminImageWidget
            return db_field.formfield(**kwargs)
        return super(ImageWidgetAdmin, self).formfield_for_dbfield(db_field, **kwargs)

用法:

class IndividualBirdAdmin(ImageWidgetAdmin):
    image_fields = ['thumbNail', 'detailImage']

图像将显示在字段中,thumbNail并且detailImage

于 2015-09-25T22:10:56.207 回答
15

使用django-imagekit你可以像这样添加任何图像:

from imagekit.admin import AdminThumbnail

@register(Fancy)
class FancyAdmin(ModelAdmin):
    list_display = ['name', 'image_display']
    image_display = AdminThumbnail(image_field='image')
    image_display.short_description = 'Image'

    readonly_fields = ['image_display']  # this is for the change form
于 2016-08-02T10:32:05.580 回答
11

虽然这里已经分享了一些很好的功能解决方案,但我觉得非表单标记,例如辅助图像标签,属于模板,而不是附加到 Django 表单小部件或在模型管理类中生成。更语义化的解决方案是:

管理模板覆盖

注意:显然我的声誉不足以发布两个以上的简单链接,因此我在以下文本中创建了注释,并在此答案的底部包含了相应的 URL。

Django 管理站点文档

覆盖管理模块用来生成管理站点各个页面的许多模板相对容易。您甚至可以为特定应用程序或特定模型覆盖其中一些模板。

Django django.contrib.admin.options.ModelAdmin(通常在命名空间下访问django.contrib.admin.ModelAdmin)提供了一系列可能的模板路径到Django 的模板加载器,从最具体到不太具体。此片段直接复制自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)

因此,考虑到前面提到的 Django 管理模板覆盖文档和模板搜索路径,假设已经创建了一个应用程序“文章”,其中定义了一个模型类“文章”。如果只想覆盖或扩展model默认 Django 管理站点更改表单articles.models.Article,可以执行以下步骤:

  1. 为覆盖文件创建一个模板目录结构。
    • 虽然文档没有提及,但是如果APP_DIRS1设置为True.
    • 因为想要通过应用标签和模型覆盖 Django 管理站点模板,所以生成的目录层次结构将是:<project_root>/articles/templates/admin/articles/article/
  2. 在新的目录结构中创建模板文件。
    • 只有管​​理员更改表单需要被覆盖,所以 create change_form.html
    • 最终的绝对路径将是<project_root>/articles/templates/admin/articles/article/change_form.html
  3. 完全覆盖或只是扩展默认的管理员更改表单模板。
    • 我无法在 Django 文档中找到有关默认管理站点模板可用的上下文数据的任何信息,因此我不得不查看 Django 源代码。
      • 默认更改表单模板:github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html
      • 一些相关的上下文字典定义可以在 django.contrib.admin.options.ModelAdmin._changeform_viewdjango.contrib.admin.options.ModelAdmin.render_change_form

我的解决方案

假设我在模型上的 ImageField 属性名称是“文件”,我实现图像预览的模板覆盖将与此类似:

{% 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似乎是从中生成 ModelForm 的模型实例。顺便说一句,我通常不使用内联 CSS,但不值得为单个规则单独文件。

资料来源:

  1. docs.djangoproject.com/en/dev/ref/settings/#app-dirs
于 2017-05-22T21:46:04.297 回答
7

我试图自己弄清楚,这就是我想出的

@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']
于 2020-07-13T17:29:32.900 回答
4

这就是它在 django 2.1 上的工作方式,无需修改models.py

在您的Hero模型中,您有一个图像字段。:

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

你可以这样做:

@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,
            )
    )
于 2018-11-11T05:26:50.453 回答
3

姜戈版本。3.0.3

模型.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'

管理员.py:

list_display = ('image_tag', )
于 2020-03-30T20:24:01.113 回答
2

@palamunder 的回答在 Django 2.2 上对我有用,但做了一些小改动。

模型.py

from django.utils.safestring import mark_safe

class AdminCategory(models.Model):
    image = models.ImageField(_("Image"),
            upload_to='categories/',
            blank=True,
            default='placeholder.png')

    def image_tag(self):
        return mark_safe('<img src="%s" width="150" height="150" />' % (
            self.image.url))  # Get Image url

    image_tag.short_description = 'Image'

管理员.py

admin.site.register(
    AdminCategory,
    list_display=["image_tag"],
)

于 2019-12-05T22:02:04.550 回答
2

Venkat Kotra答案的 Django 2.1 更新。答案在 Django 2.0.7 及更低版本上运行良好。但给出服务器 500 错误(如果DEBUG=False)或给出

render() got an unexpected keyword argument 'renderer'

原因是在 Django 2.1 中:Support for Widget.render() methods without the renderer argument is removed. 所以,paramrenderer现在是强制性的。我们必须更新函数render()AdminImageWidget包含 param renderer。它必须在之后attrskwargs如果有的话,在之前):

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))
于 2018-08-21T16:18:44.107 回答
0

如果您需要在保存前显示图像预览,您可以使用自定义 django 模板 + js

管理员.py

class UploadedImagePreview(object):
    short_description = _('Thumbnail')
    allow_tags = True

    def __init__(self, image_field, template, short_description=None, width=None, height=None):
        self.image_field = image_field
        self.template = template
        if short_description:
            self.short_description = short_description
        self.width = width or 200
        self.height = height or 200

    def __call__(self, obj):
        try:
            image = getattr(obj, self.image_field)
        except AttributeError:
            raise Exception('The property %s is not defined on %s.' %
                (self.image_field, obj.__class__.__name__))

        template = self.template

        return render_to_string(template, {
            'width': self.width,
            'height': self.height,
            'watch_field_id': 'id_' + self.image_field  # id_<field_name> is default ID 
                                                        # for ImageField input named `<field_name>` (in Django Admin) 
        })


@admin.register(MyModel)
class MainPageBannerAdmin(ModelAdmin):
    image_preview = UploadedImagePreview(image_field='image', template='admin/image_preview.html',
                                         short_description='uploaded image', width=245, height=245)
    readonly_fields = ('image_preview',)
    
    fields = (('image', 'image_preview'), 'title')

image_preview.html

<img id="preview_{{ watch_field_id }}" style="display: none; width: {{ width }}px; height: {{ height }}px" alt="">

<script>
    function handleFileSelect(event) {
        var files = event.target.files; // FileList object
        // Loop through the FileList and render image files as thumbnails
        for (var i = 0, f; f = files[i]; i++) {
            // Only process image files
            if (!f.type.match('image.*')) continue;
            // Init FileReader()
            // See: https://developer.mozilla.org/en-US/docs/Web/API/FileReader
            var reader = new FileReader();
            // Closure to capture the file information
            reader.onload = (function () {
                return function (e) {
                    // Render background image
                    document.getElementById('preview_{{watch_field_id}}').src = e.target.result;
                    // Set `display: block` to preview image container
                    document.getElementById('preview_{{watch_field_id}}').style.display = 'block';
                };
            })(f);
            // Read in the image file as a data URL
            reader.readAsDataURL(f);
        }
    }

    // Change img src after change file input
    // watch_field_id — is ID for ImageField input
    document.getElementById('{{ watch_field_id }}').addEventListener('change', handleFileSelect, false);
</script>
于 2020-06-22T12:09:34.043 回答
0

在 Django v3.2 上测试。*

  • 只是你可以在你的这个代码model.py
from django.db import models
from django.utils.html import mark_safe


class Book(models.Model):
        image = models.ImageField()

        def image_tag(self):
                if self.image != '':
                    return mark_safe('<img src="%s%s" width="150" height="150" />' % (f'{settings.MEDIA_URL}', self.image))
  • 然后添加这个admin.py
list_display = ['image_tag']
于 2021-11-25T21:29:26.237 回答