虽然我可以在 list_display 中显示上传的图像,但是否可以在每个模型页面上执行此操作(如在更改模型的页面中)?
一个快速的示例模型是:
Class Model1(models.Model):
image = models.ImageField(upload_to=directory)
默认管理员显示上传图片的 url,但不显示图片本身。
谢谢!
虽然我可以在 list_display 中显示上传的图像,但是否可以在每个模型页面上执行此操作(如在更改模型的页面中)?
一个快速的示例模型是:
Class Model1(models.Model):
image = models.ImageField(upload_to=directory)
默认管理员显示上传图片的 url,但不显示图片本身。
谢谢!
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.
除了 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']
无需修改模型即可在管理员中完成
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',]
对于 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
使用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
虽然这里已经分享了一些很好的功能解决方案,但我觉得非表单标记,例如辅助图像标签,属于模板,而不是附加到 Django 表单小部件或在模型管理类中生成。更语义化的解决方案是:
注意:显然我的声誉不足以发布两个以上的简单链接,因此我在以下文本中创建了注释,并在此答案的底部包含了相应的 URL。
覆盖管理模块用来生成管理站点各个页面的许多模板相对容易。您甚至可以为特定应用程序或特定模型覆盖其中一些模板。
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
,可以执行以下步骤:
APP_DIRS
1设置为True
.<project_root>/articles/templates/admin/articles/article/
change_form.html
。<project_root>/articles/templates/admin/articles/article/change_form.html
django.contrib.admin.options.ModelAdmin._changeform_view
和django.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,但不值得为单个规则单独文件。
我试图自己弄清楚,这就是我想出的
@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']
这就是它在 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,
)
)
姜戈版本。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', )
@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"],
)
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
。它必须在之后attrs
(kwargs
如果有的话,在之前):
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))
如果您需要在保存前显示图像预览,您可以使用自定义 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>
在 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']