Skip to content

fix: infinite recursion bug on django-polymorphic >=4.9#1571

Merged
fsbraun merged 1 commit intodjango-cms:masterfrom
bckohan:polymorphic-bug
Jan 17, 2026
Merged

fix: infinite recursion bug on django-polymorphic >=4.9#1571
fsbraun merged 1 commit intodjango-cms:masterfrom
bckohan:polymorphic-bug

Conversation

@bckohan
Copy link
Contributor

@bckohan bckohan commented Jan 15, 2026

Description

closes #1569

There are a few things going on here:

  1. Prior to django-polymorphic 4.9 the base_manager (used for loaddata and related fields) was set to the same as the default_manager by default. This was incongruous with Django's behavior so it was fixed in django-polymorphic/#816.
  2. A custom PolymorphicQuerySet is used on File objects as the default manager. It overrides only() to always add a few fields including _file_size and is_public because these fields are accessed in the model's __init__ method. I suspect this infinite recursion bug was encountered before, hence the override.
  3. If you delete a Folder object, internally Django does something like: folder.all_files.only("id").delete(). This triggered the bug because it was now using a plain PolymorphicManager that did not add the extra fields to only().
  4. The fix, implemented in this PR is to set base_manager_name = "objects" to force related managers to use the overridden only() implementation.
  5. There was another bug in django-polymorphic 4.9, 4.10.0-4.10.2 that prevented Meta.base_manager_name from being respected. This has been fixed in 4.9.1 and 4.10.3. The offending releases were yanked.

This PR, combined with the fixes in 4.9.1 and 4.10.3 should fix everything. It will also work on versions prior to 4.9.

You probably want to merge this PR soon because if anyone installs django-polymorphic 4.9+ with the existing release they will not be able to delete Folders that aren't empty. I am also a user of this great library so thank you!!

I also restricted the django-polymorphic dependency to < 5. As the maintainer I can attest that there will be breaking changes (but for great reasons!)

Related resources

Checklist

  • I have opened this pull request against master
  • I have added or modified the tests when changing logic
  • I have followed the conventional commits guidelines to add meaningful information into the changelog
  • I have read the contribution guidelines and I have joined #workgroup-pr-review on
    Slack to find a “pr review buddy” who is going to review my pull request.

Summary by Sourcery

Update filer File model manager configuration to align with newer django-polymorphic behavior and prevent recursion issues when deleting related objects.

Bug Fixes:

  • Ensure related managers for File use the custom default manager by setting base_manager_name to the objects manager, preventing infinite recursion when deleting folders with newer django-polymorphic versions.

Build:

  • Relax django-polymorphic dependency to allow all 4.x releases while remaining below 5.0.

Chores:

  • Add a Django migration to persist the updated File model options in the database schema history.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 15, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Aligns File model manager configuration with newer django-polymorphic behavior to avoid infinite recursion when accessing related managers, and updates package constraints and schema to persist the new base manager setting.

Sequence diagram for Folder deletion using File base_manager_name

sequenceDiagram
    actor Admin as Admin
    participant FolderModel as Folder
    participant DjangoORM as Django_ORM
    participant FileRelatedManager as File_related_manager
    participant FileObjectsManager as File_objects_manager
    participant FileQuerySet as PolymorphicQuerySet_with_only_override

    Admin->>DjangoORM: call delete on Folder instance
    DjangoORM->>FolderModel: Folder.delete()
    FolderModel->>FileRelatedManager: all_files.only(id).delete()

    Note over FileRelatedManager,FileObjectsManager: After this PR base_manager_name = objects

    FileRelatedManager->>FileObjectsManager: resolve base manager objects
    FileObjectsManager->>FileQuerySet: only(id)
    FileQuerySet-->>FileObjectsManager: QuerySet with id,_file_size,is_public
    FileObjectsManager->>FileQuerySet: delete()
    FileQuerySet-->>DjangoORM: cascade delete File rows without recursion
Loading

Class diagram for updated File model Meta options

classDiagram
    class File{
        +FileMeta Meta
    }

    class FileMeta{
        +string app_label = filer
        +string verbose_name = file
        +string verbose_name_plural = files
        +string base_manager_name = objects
    }

    File *-- FileMeta
Loading

File-Level Changes

Change Details Files
Ensure related managers on File use the custom PolymorphicQuerySet (with the overridden only()) by making it the model’s base manager.
  • Set Meta.base_manager_name to 'objects' on the File model so that related managers use the custom default manager instead of the plain PolymorphicManager.
  • Add a schema migration altering File model options to include base_manager_name while preserving verbose_name settings.
filer/models/filemodels.py
filer/migrations/0018_alter_file_options.py
Allow installing newer django-polymorphic versions that include upstream fixes while keeping an upper bound before 5.0.
  • Relax django-polymorphic dependency upper bound from <4.9.0 to <5.0 to permit 4.9.x and 4.10.x versions with the base_manager_name bugfix.
pyproject.toml

Possibly linked issues

  • #bug: Infinite recursion with django-polymorphic>=4.9: The PR’s base_manager_name fix and dependency bump directly address the infinite recursion seen with django-polymorphic>=4.9.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@bckohan bckohan marked this pull request as draft January 15, 2026 17:59
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link

codecov bot commented Jan 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.98%. Comparing base (ab77d25) to head (d909da1).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1571      +/-   ##
==========================================
+ Coverage   76.95%   76.98%   +0.03%     
==========================================
  Files          77       78       +1     
  Lines        3666     3671       +5     
  Branches      498      498              
==========================================
+ Hits         2821     2826       +5     
  Misses        675      675              
  Partials      170      170              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bckohan bckohan marked this pull request as ready for review January 16, 2026 00:29
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The django-polymorphic version constraint now allows 4.9.0 and 4.10.0–4.10.2, which you’ve identified as broken; consider explicitly excluding these versions (e.g. using != constraints) so that users can’t install them accidentally.
  • The AlterModelOptions migration hardcodes verbose_name and verbose_name_plural; if these are ever changed on the model, remember that this migration will still reflect the old values, so it may be safer to generate the migration from the current model state rather than editing by hand.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The django-polymorphic version constraint now allows 4.9.0 and 4.10.0–4.10.2, which you’ve identified as broken; consider explicitly excluding these versions (e.g. using `!=` constraints) so that users can’t install them accidentally.
- The `AlterModelOptions` migration hardcodes `verbose_name` and `verbose_name_plural`; if these are ever changed on the model, remember that this migration will still reflect the old values, so it may be safer to generate the migration from the current model state rather than editing by hand.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@bckohan
Copy link
Contributor Author

bckohan commented Jan 16, 2026

  • The django-polymorphic version constraint now allows 4.9.0 and 4.10.0–4.10.2, which you’ve identified as broken; consider explicitly excluding these versions (e.g. using != constraints) so that users can’t install them accidentally.

4.9.0 and 4.10.0, 4.10.1, 4.10.2 were all yanked so this should be fine.

@bckohan
Copy link
Contributor Author

bckohan commented Jan 16, 2026

  • The AlterModelOptions migration hardcodes verbose_name and verbose_name_plural; if these are ever changed on the model, remember that this migration will still reflect the old values, so it may be safer to generate the migration from the current model state rather than editing by hand.

Not valid.

@bckohan bckohan changed the title fix infinite recursion bug on django-polymorphic >=4.9 fix: infinite recursion bug on django-polymorphic >=4.9 Jan 16, 2026
Copy link
Member

@fsbraun fsbraun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @bckohan ! Thank you so much for finding the root cause of this bug!

@fsbraun fsbraun merged commit 63ab81c into django-cms:master Jan 17, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Infinite recursion with django-polymorphic>=4.9

2 participants