Reduce modeladmin’s dependency on wagtail.wagtailimages and wagtail.wagtaildocs

- Remove imports at the top of options.py and views.py that result in ImportError when those apps aren’t installed
- Alter ThumbnailMixin and InspectView to use the `wagtail.wagtailimages.shortcuts.get_rendition_or_not_found()` method to render images, which handles missing image source files gracefully, and reduces code duplication.
- Simplify `get_field_display_value()` by not limiting image and document rendering to ForeignKey fields. It should work consistently for property methods or other attributes too.
This commit is contained in:
Andy Babic 2017-02-08 20:22:10 +00:00 committed by Matt Westcott
parent 449c578c38
commit 3348cd5c89
5 changed files with 55 additions and 43 deletions

View file

@ -24,6 +24,7 @@ Changelog
* Fix: Choosing a popular search term for promoted search results now works correctly after pagination (Janneke Janssen)
* Fix: IDs used in tabbed interfaces are now namespaced to avoid collisions with other page elements (Janneke Janssen)
* Fix: Page title not displaying page name when moving a page (Trent Holliday)
* Fix: The ModelAdmin module can now work without the wagtailimages and wagtaildocs apps installed (Andy Babic)
1.9 (16.02.2017)

View file

@ -28,16 +28,19 @@ class:
------------------------------------------
**Expected value:** A list or tuple, where each item is the name of a field
that you'd like ``InpectView`` to render.
or attribute on the instance that you'd like ``InpectView`` to render.
A sensible value will be rendered for most field types.
If a field happens to be a ``ForeignKey`` linking to the
``wagtailimages.Image`` model, a thumbnail of that image will be rendered.
If a field happens to be a ``ForeignKey`` linking to the
``wagtaildocs.Document`` model, a link to that document will be rendered.
If you have ``wagtail.wagtailimages`` installed, and the value happens to be an
instance of ``wagtailimages.models.Image`` (or a custom model that subclasses
``wagtailimages.models.AbstractImage``), a thumbnail of that image will be
rendered.
If you have `wagtail.wagtaildocs` installed, and the value happens to be an
instance of `wagtaildocs.models.Document` (or a custom model that subclasses
``wagtaildocs.models.AbstractDocument``), a link to that document will be
rendered, along with the document title, file extension and size.
.. _modeladmin_inspect_view_fields_exclude:

View file

@ -38,6 +38,7 @@ Bug fixes
* Choosing a popular search term for promoted search results now works correctly after pagination (Janneke Janssen)
* IDs used in tabbed interfaces are now namespaced to avoid collisions with other page elements (Janneke Janssen)
* Page title not displaying page name when moving a page (Trent Holliday)
* The ModelAdmin module can now work without the wagtailimages and wagtaildocs apps installed (Andy Babic)
Upgrade considerations

View file

@ -1,5 +1,6 @@
from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.conf.urls import url
from django.contrib.auth.models import Permission
from django.core.exceptions import ImproperlyConfigured
@ -10,7 +11,6 @@ from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Page
from wagtail.wagtailimages.models import Filter
from .helpers import (
AdminURLHelper, ButtonHelper, PageAdminURLHelper, PageButtonHelper, PagePermissionHelper,
@ -72,6 +72,14 @@ class ThumbnailMixin(object):
thumb_col_header_text = _('image')
thumb_default = None
def __init__(self, *args, **kwargs):
if 'wagtail.wagtailimages' not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
u"The `wagtail.wagtailimages` app must be installed in order "
"to use the `ThumbnailMixin` class."
)
super(ThumbnailMixin, self).__init__(*args, **kwargs)
def admin_thumb(self, obj):
try:
image = getattr(obj, self.thumb_image_field_name, None)
@ -86,13 +94,17 @@ class ThumbnailMixin(object):
'width': self.thumb_image_width,
'class': self.thumb_classname,
}
if image:
fltr = Filter(spec=self.thumb_image_filter_spec)
img_attrs.update({'src': image.get_rendition(fltr).url})
return mark_safe('<img{}>'.format(flatatt(img_attrs)))
elif self.thumb_default:
return mark_safe('<img{}>'.format(flatatt(img_attrs)))
return ''
if not image:
if self.thumb_default:
return mark_safe('<img{}>'.format(flatatt(img_attrs)))
return ''
# try to get a rendition of the image to use
from wagtail.wagtailimages.shortcuts import get_rendition_or_not_found
spec = self.thumb_image_filter_spec
rendition = get_rendition_or_not_found(image, spec)
img_attrs.update({'src': rendition.url})
return mark_safe('<img{}>'.format(flatatt(img_attrs)))
admin_thumb.short_description = thumb_col_header_text

View file

@ -35,9 +35,6 @@ from django.views.generic.edit import FormView
from wagtail.wagtailadmin import messages
from wagtail.wagtailadmin.edit_handlers import (
ObjectList, extract_panel_definitions_from_model_class)
from wagtail.wagtaildocs.models import get_document_model
from wagtail.wagtailimages import get_image_model
from wagtail.wagtailimages.models import Filter
from .forms import ParentChooserForm
@ -850,7 +847,7 @@ class InspectView(InstanceSpecificView):
return label
def get_field_display_value(self, field_name, field=None):
""" Return a display value for a field """
""" Return a display value for a field/attribute """
# First we check for a 'get_fieldname_display' property/method on
# the model, and return the value of that, if present.
@ -860,39 +857,37 @@ class InspectView(InstanceSpecificView):
return val_funct()
return val_funct
# If we have a real field, we can utilise that to try to display
# something more useful
if field is not None:
try:
field_type = field.get_internal_type()
if (
field_type == 'ForeignKey' and
field.related_model == get_image_model()
):
# The field is an image
return self.get_image_field_display(field_name, field)
# Now let's get the attribute value from the instance itself and see if
# we can render something useful. raises AttributeError appropriately.
val = getattr(self.instance, field_name)
if (
field_type == 'ForeignKey' and
field.related_model == get_document_model()
):
# The field is a document
return self.get_document_field_display(field_name, field)
# wagtail.wagtailimages might not be installed
try:
from wagtail.wagtailimages.models import AbstractImage
if isinstance(val, AbstractImage):
# Render a rendition of the image
return self.get_image_field_display(field_name, field)
except RuntimeError:
pass
except AttributeError:
pass
# wagtail.wagtaildocuments might not be installed
try:
from wagtail.wagtaildocs.models import AbstractDocument
if isinstance(val, AbstractDocument):
# Render a link to the document
return self.get_document_field_display(field_name, field)
except RuntimeError:
pass
# Resort to getting the value of 'field_name' from the instance
return getattr(self.instance, field_name,
self.model_admin.get_empty_value_display(field_name))
# Resort to returning the real value or 'empty value'
return val or self.model_admin.get_empty_value_display(field_name)
def get_image_field_display(self, field_name, field):
""" Render an image """
from wagtail.wagtailimages.shortcuts import get_rendition_or_not_found
image = getattr(self.instance, field_name)
if image:
fltr = Filter(spec='max-400x400')
rendition = image.get_rendition(fltr)
return rendition.img_tag
return get_rendition_or_not_found(image, 'max-400x400').img_tag
return self.model_admin.get_empty_value_display(field_name)
def get_document_field_display(self, field_name, field):