Source code for flex_menu.utils
"""Helper functions for URL resolution and parameter extraction."""
import contextlib
from django.urls import get_resolver
from django.urls.exceptions import NoReverseMatch
[docs]
def get_required_url_params(view_name: str) -> frozenset:
"""Return the required URL parameter names for a Django view name.
Supports both simple view names ("home") and namespaced view names
("app:home"). Parameters captured by parent resolver prefixes (e.g.
"<str:uuid>/" in an include() prefix) are included.
Relies on Django's own pre-built resolver structures (namespace_dict,
reverse_dict), so each call is O(1) dict lookups with no caching needed.
Args:
view_name: A Django view name as used in reverse(), optionally
namespaced (e.g. "app:home").
Returns:
The names of the required URL parameters, e.g. {"pk"} or {"slug"}.
Raises:
NoReverseMatch: If the namespace or view name cannot be found.
"""
resolver = get_resolver()
parts = view_name.split(":")
name = parts[-1]
namespaces = parts[:-1]
accumulated_params: set[str] = set()
current = resolver
for ns in namespaces:
entry = current.namespace_dict.get(ns)
if entry is None:
raise NoReverseMatch(f"Namespace ''{ns}'' not found in ''{view_name}''.")
_app_name, sub_resolver = entry
pat = sub_resolver.pattern
if hasattr(pat, "converters") and pat.converters:
accumulated_params.update(pat.converters.keys())
elif hasattr(pat, "regex"):
# A pattern without a compiled regex contributes no named params.
with contextlib.suppress(Exception):
accumulated_params.update(pat.regex.groupindex.keys())
current = sub_resolver
# reverse_dict.get() returns a 4-tuple (bits, p_pattern, defaults, converters)
# where bits = [(format_str, [param_names]), ...].
matches = current.reverse_dict.get(name)
if matches is None:
raise NoReverseMatch(f"No URL pattern found for view name ''{view_name}''.")
bits, _p_pattern, _defaults, _converters = matches
local_params: set[str] = set()
for _fmt, param_names in bits:
local_params.update(param_names)
return frozenset(accumulated_params | local_params)