pallets-eco / pallets-eco/flask-admin

fix(peewee): QueryAjaxModelLoader.get_one() raises DoesNotExist for a stale/absent pk

Open
#2,940 0 comments 0 reactions 1 assignee View on GitHub

@hasansezertasan is already working on this.

Since Jul 31, 2026.

bug
Dominant language
Python
Stars
6.1k
Forks
1.6k
Avg merge
6d 14h
Merged PRs (30d)
1

Description

Summary

QueryAjaxModelLoader.get_one() in the peewee backend calls self.model.get(...), which raises peewee's DoesNotExist when no row matches. Its caller, AjaxSelectField._get_data, guards with if model is not None — expecting None, not an exception — so a form referencing a since-deleted (or otherwise unmatched) pk crashes instead of rendering with no selection.

This is the autocomplete/ajax-select twin of the view-layer bug fixed in #2939 / #2924. That PR fixed ModelView.get_one(); this one was left deliberately out of scope there.

Location
flask_admin/contrib/peewee/ajax.py:57   get_one -> self.model.get(**{self.pk: pk})
flask_admin/model/fields.py:195          AjaxSelectField._get_data -> model = self.loader.get_one(...)
flask_admin/model/fields.py:197          if model is not None:   # expects None on miss
Other backends

Both sibling loaders already return None on a miss, so peewee is the only one that lets the exception escape:

  • sqla — flask_admin/contrib/sqla/ajax.py:90: session.get(self.model, pk) returns None
  • mongoengine — flask_admin/contrib/mongoengine/ajax.py:69: self.model.objects.filter(pk=pk).first() returns None
Proposed fix

Catch the module-level peewee.DoesNotExist in QueryAjaxModelLoader.get_one and return None, mirroring the fix in #2939:

def get_one(self, pk: t.Any) -> t.Any:
    try:
        return self.model.get(**{self.pk: pk})
    except DoesNotExist:
        return None

(Module-level DoesNotExist rather than self.model.DoesNotExist — each model subclasses it, and the module-level name is what passes mypy --strict.)

Scope note

As with #2939, a malformed pk against an integer primary key raises no error on sqlite (DoesNotExist covers it) but would raise a driver-specific DataError on Postgres. That's left out here too rather than importing driver exceptions into the backend.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.