Skip to content

Replace deprecated sorl-thumbnail is_default with exists() in cover_image_* methods #1135

@rdhyee

Description

@rdhyee

Background

While bringing dj42.unglue.it back online (2026-04-28) for #1123 testing, surfaced a latent bug in core/models/bibmodels.py. The Edition.cover_image_* methods reference ImageFile.is_default, which was removed from sorl-thumbnail in the 12.6 → 12.11 upgrade (#1123 bumps sorl-thumbnail 12.6.3 → 12.11.0).

The bug

Three methods on Edition use the deprecated attribute:

core/models/bibmodels.py:896-915

def cover_image_small(self):
    if self.cover_image:
        im = get_thumbnail(self.cover_image, 'x80', crop='noop', quality=95)
        if not im.is_default:        # AttributeError in sorl 12.11.0
            return im.url
    if self.googlebooks_id:
        return "https://encrypted.google.com/books?id=%s..." % self.googlebooks_id
    return DEFAULT_COVER_SMALL

def cover_image_thumbnail(self):     # same pattern with 'x128'
    ...

def cover_image_large(self):         # also same pattern (line ~890)
    ...

In sorl 12.11.0, BaseImageFile/ImageFile/DummyImageFile no longer expose is_default. The replacement is im.exists() (a method that returns whether the thumbnail file actually exists).

Why dj42 still appears to render covers

Despite the missing attribute, on dj42 (running #1123's branch) the methods do return correct cover URLs — at least for warm code paths. Direct probe of im.is_default reliably raises AttributeError, but execution traces (sys.settrace) show line 900 evaluating without raising during normal method invocation. Likely related to sorl's KVStore state — the AttributeError is intermittent rather than deterministic, which is more dangerous than a clean failure.

This means current behavior is fragile/heisenbug-prone:

  • Cold thumbnail cache: may surface the AttributeError → 500 on the page
  • Warm cache: silently works but on a deprecated API path

Proposed fix

Three-call-site mechanical change:

-        if not im.is_default:
+        if im.exists():

Applied to all three cover_image_* methods (small, thumbnail, large). The semantic is identical: "if the generated thumbnail file actually exists, return its URL; otherwise fall through to the Google Books / DEFAULT_COVER fallback."

Reference: sorl-thumbnail 12.11.0 release notes — no formal CHANGELOG entry for is_default removal but the attribute is gone from the source.

Discovered via

Comparison testing of dj42 (Django 4.2 / sorl 12.11.0) against test (Django 1.11 / sorl 12.6.3) for the same DB content. Same work, same edition, same cover_image field — both stacks return identical cover URLs (no behavioral regression for end users), but the dj42 path goes through the deprecated attribute access.

Test plan when fixing

  • On dj42 with cold cache (clear sorl KVStore): verify Edition.cover_image_small/thumbnail/large() for at least 3 editions with valid cover_image — should return valid URL, not raise
  • On dj42 with warm cache: same, no regression
  • On dj42 for editions WITHOUT cover_image but WITH googlebooks_id: should fall back to Google Books URL
  • On dj42 for editions with neither: should return DEFAULT_COVER_*

Scope

Only core/models/bibmodels.py (3 call sites, one file). Trivial change but should be in #1123 (or a fast follow-up PR against fix/django-upgrade-libraryauth) before the migration cuts over to prod.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions