The Page Registry

About this document

This is the API reference for the resume page extension point: the ResumePage base class, the PageRegistry, capability-based section selection, page discovery, and the navigation template tags.

For a task-oriented how-to (defining a page, templates, navigation, shipping a page as its own package) see Creating Page Plugins. For the design rationale see Pluggable Resume Pages. The three built-in pages (CoverLetterPage, CvPage, PermissionDeniedPage) are documented in The Views.

Overview

A page is a registered ResumePage subclass that owns a route under <slug:slug>/, the section plugins it renders, access control, navigation metadata, and themed-template selection. Pages mirror content plugins: there is a PageRegistry and a module-level page_registry singleton, exactly as The Plugin Registry describes for plugins. Page content keeps living in the section plugins’ Resume.plugin_data, so adding a page needs no model and no migration.

ResumePage

class django_resume.pages.ResumePage

Base class for a registered resume page. Subclass it, set the class attributes, and override the hooks you need.

url_name

The URL name used for reverse() (e.g. reverse("django_resume:cv", kwargs={"slug": slug})). It is also the registry key, so it must be unique across registered pages.

path

The sub-segment appended to <slug:slug>/. Use "" for the default/root page; the bare <slug:slug>/ catch-all is always emitted last by PageRegistry.get_urls(), so more specific paths never shadow it (and it never shadows them).

template_name

Resolved as django_resume/pages/{theme}/{template_name} where theme is resolve_page_theme() of the resume — the active theme if it ships the template, otherwise the plain fallback.

section_names

Which section plugins the page builds context for. Accepts an explicit list of plugin names (["identity", "about"]), the string "__all__" (every registered section plugin), or a ByCapability selector from by_capability(). It is an inclusion filter, not an ordering — the template decides layout by including sections by name.

nav_title

Human-friendly label for navigation menus. The empty default means the page is reachable by URL but not advertised in navigation.

nav_order

Integer navigation order; lower sorts first. The sort is stable, so pages sharing a value keep registration order. Default 0. The built-ins use 10 (Cover), 20 (CV), 30 (the 403 editor).

nav_group

Navigation group label. Links sharing a nav_group render together under page_nav_groups(). The empty default keeps the page in the implicit ungrouped bucket.

check_access(request, resume)

Return None to proceed, or an HttpResponse to short-circuit (e.g. a 403 or a login redirect). Default: None.

is_visible(resume)

Whether the page is advertised in navigation for resume. Override for state-dependent links (the built-in 403 editor returns resume.token_is_required). Default: True.

nav_url(resume)

The page’s URL for resume, reversed via the django_resume application namespace.

get_context(request, resume, *, base_context)

Build the template context. The default resolves the theme (with plain fallback) and calls build_section_context() for section_names, so the section fragments render through the same theme the page frame resolves to.

serve(request, resume, base_context)

Build the page response. The default renders template_name for the resolved theme with the context from get_context(). Override to take full control of rendering (for example to return an HttpResponse without a themed template).

finalize_response(response, request, resume)

Post-process every response the dispatcher returns — including a short-circuit response from check_access() — e.g. to set headers. Default: returns response unchanged.

Selecting sections by capability

django_resume.pages.by_capability(*capabilities, match='any')

Build a ByCapability selector for ResumePage.section_names. With match="any" (the default) a plugin is included if it carries at least one of capabilities; with match="all" it must carry every one. An empty capability set matches nothing. Any other match value raises ValueError.

section_names = by_capability("portfolio")
section_names = by_capability("portfolio", "cv", match="all")

Section plugins advertise their tags through Plugin.capabilities (a tuple[str, ...], empty by default). Access/UI-control plugins such as token and theme carry no capabilities and are never selected as content.

class django_resume.pages.ByCapability

The selector returned by by_capability(); a frozen dataclass with capabilities and match fields and a matches(plugin) predicate. Prefer the by_capability() factory at call sites.

PageRegistry

class django_resume.pages.PageRegistry

A registry of resume page classes, instantiated once as the page_registry singleton.

register(page_class)

Instantiate page_class and store it under its url_name. Re-registering the same url_name replaces the entry.

register_page_list(page_classes)

Call register() for each class in page_classes.

unregister(page_class)

Remove the page registered under page_class.url_name (a no-op if absent).

get_page(url_name)

Return the registered page instance for url_name, or None.

get_all_pages()

Return all registered page instances in registration order.

get_ordered_pages()

Return all registered pages sorted by nav_order. The sort is stable, so equal orders keep registration order — navigation is deterministic for built-ins plus any third-party pages.

get_urls()

Return the generated URLPattern list, one <slug:slug>/{path} per page, with the bare <slug:slug>/ (path == "") catch-all emitted last by construction. django_resume.urls splices this into its urlpatterns.

django_resume.pages.page_registry

The module-level singleton instance of PageRegistry, shared across the application.

Discovery

Built-in pages are registered in AppConfig.ready before plugin registration. Third-party pages are then discovered two ways, both before django_resume.urls is imported (which evaluates PageRegistry.get_urls() once), so every page has a route before the URLconf freezes.

django_resume.pages.autodiscover_pages()

Import each installed app’s top-level resume_pages module (mirroring Django admin’s autodiscover_modules) so apps register pages by import side-effect, then call load_entry_point_pages().

django_resume.pages.load_entry_point_pages()

Register pages contributed by separately distributed packages that are not installed Django apps, through ENTRY_POINT_GROUP entry points. Each entry point loads to either a ResumePage subclass (registered directly) or a zero-argument callable (invoked so it can register its own pages); any other target raises TypeError. Loading is idempotent.

django_resume.pages.ENTRY_POINT_GROUP

The entry-point group name, "django_resume.pages". A distribution declares an entry point in this group:

[project.entry-points."django_resume.pages"]
contact = "mypkg.pages:ContactPage"