Skip to content

Commit afc43ec

Browse files
committed
fix: patch Wagtail API page-ID filters to accept ObjectId hex strings
ChildOfFilter, AncestorOfFilter, DescendantOfFilter, and TranslationOfFilter all call int(value) and raise 400 BadRequestError when given a 24-char ObjectId hex string like child_of=69f16d9b.... Patch all four methods to try ObjectId parsing as a fallback when int() fails.
1 parent 1a5dc41 commit afc43ec

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

  • src/dbx_python_cli/templates/project_template/project_name/settings/apps

src/dbx_python_cli/templates/project_template/project_name/settings/apps/wagtail.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,106 @@ def _patched_json_default(self, o, _orig=DjangoJSONEncoder.default):
7373
except ImportError:
7474
pass
7575

76+
# Patch Wagtail API v2 page-ID filters to accept 24-char ObjectId hex
77+
# strings in addition to integers. All four filters call int(value)
78+
# and raise BadRequestError on ValueError, rejecting ObjectId strings.
79+
try:
80+
from bson import ObjectId
81+
from wagtail.api.v2 import filters as _api_filters
82+
from wagtail.api.v2.utils import BadRequestError as _BadReq
83+
from wagtail.models import Page as _Page
84+
85+
def _parse_page_id(value):
86+
try:
87+
n = int(value)
88+
if n < 0:
89+
raise ValueError()
90+
return n
91+
except ValueError:
92+
return ObjectId(value)
93+
94+
def _child_of_filter(self, request, queryset, view):
95+
if "child_of" not in request.GET:
96+
return queryset
97+
value = request.GET["child_of"]
98+
if value == "root":
99+
parent_page = view.get_root_page()
100+
else:
101+
try:
102+
parent_page = view.get_base_queryset().get(
103+
id=_parse_page_id(value)
104+
)
105+
except _Page.DoesNotExist as e:
106+
raise _BadReq("parent page doesn't exist") from e
107+
except Exception as e:
108+
raise _BadReq("child_of must be a positive integer") from e
109+
queryset = queryset.child_of(parent_page)
110+
queryset._filtered_by_child_of = parent_page
111+
return queryset
112+
113+
def _ancestor_of_filter(self, request, queryset, view):
114+
if "ancestor_of" not in request.GET:
115+
return queryset
116+
value = request.GET["ancestor_of"]
117+
try:
118+
descendant_page = view.get_base_queryset().get(
119+
id=_parse_page_id(value)
120+
)
121+
except _Page.DoesNotExist as e:
122+
raise _BadReq("descendant page doesn't exist") from e
123+
except Exception as e:
124+
raise _BadReq("ancestor_of must be a positive integer") from e
125+
return queryset.ancestor_of(descendant_page)
126+
127+
def _descendant_of_filter(self, request, queryset, view):
128+
if "descendant_of" not in request.GET:
129+
return queryset
130+
if hasattr(queryset, "_filtered_by_child_of"):
131+
raise _BadReq(
132+
"filtering by descendant_of with child_of is not supported"
133+
)
134+
value = request.GET["descendant_of"]
135+
if value == "root":
136+
parent_page = view.get_root_page()
137+
else:
138+
try:
139+
parent_page = view.get_base_queryset().get(
140+
id=_parse_page_id(value)
141+
)
142+
except _Page.DoesNotExist as e:
143+
raise _BadReq("ancestor page doesn't exist") from e
144+
except Exception as e:
145+
raise _BadReq("descendant_of must be a positive integer") from e
146+
return queryset.descendant_of(parent_page)
147+
148+
def _translation_of_filter(self, request, queryset, view):
149+
if "translation_of" not in request.GET:
150+
return queryset
151+
value = request.GET["translation_of"]
152+
if value == "root":
153+
page = view.get_root_page()
154+
else:
155+
try:
156+
page = view.get_base_queryset().get(id=_parse_page_id(value))
157+
except _Page.DoesNotExist as e:
158+
raise _BadReq("translation_of page doesn't exist") from e
159+
except Exception as e:
160+
raise _BadReq(
161+
"translation_of must be a positive integer"
162+
) from e
163+
saved = getattr(queryset, "_filtered_by_child_of", None)
164+
queryset = queryset.translation_of(page)
165+
if saved:
166+
queryset._filtered_by_child_of = saved
167+
return queryset
168+
169+
_api_filters.ChildOfFilter.filter_queryset = _child_of_filter
170+
_api_filters.AncestorOfFilter.filter_queryset = _ancestor_of_filter
171+
_api_filters.DescendantOfFilter.filter_queryset = _descendant_of_filter
172+
_api_filters.TranslationOfFilter.filter_queryset = _translation_of_filter
173+
except ImportError:
174+
pass
175+
76176
# Patch ModelViewSet.pk_path_converter so viewsets whose model uses
77177
# ObjectIdAutoField get "object_id" URL converter instead of "int".
78178
# ObjectIdAutoField inherits AutoField → IntegerField, so Wagtail's

0 commit comments

Comments
 (0)