# https://github.com/djblets/djblets Project Manual

Generated at: 2026-07-04 04:59:01 UTC

## Table of Contents

- [Djblets Overview and Module Architecture](#page-1)
- [Extension Framework: Writing and Testing Extensions](#page-2)
- [Caching: Backends, Keys, Locks, and Synchronization](#page-3)
- [WebAPI Resources, Datagrids, Forms, and Registries](#page-4)

<a id='page-1'></a>

## Djblets Overview and Module Architecture

### Related Pages

Related topics: [Extension Framework: Writing and Testing Extensions](#page-2), [Caching: Backends, Keys, Locks, and Synchronization](#page-3), [WebAPI Resources, Datagrids, Forms, and Registries](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.rst](https://github.com/djblets/djblets/blob/main/README.rst)
- [djblets/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/__init__.py)
- [djblets/_version.py](https://github.com/djblets/djblets/blob/main/djblets/_version.py)
- [pyproject.toml](https://github.com/djblets/djblets/blob/main/pyproject.toml)
- [djblets/dependencies.py](https://github.com/djblets/djblets/blob/main/djblets/dependencies.py)
- [djblets/cache/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/cache/__init__.py)
- [djblets/datagrid/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/datagrid/__init__.py)
- [djblets/extensions/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/__init__.py)
- [djblets/configforms/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/configforms/__init__.py)
- [djblets/pipeline/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/pipeline/__init__.py)
- [djblets/webapi/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/webapi/__init__.py)
- [djblets/siteconfig/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/siteconfig/__init__.py)
- [djblets/mail/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/mail/__init__.py)
- [djblets/forms/widgets.py](https://github.com/djblets/djblets/blob/main/djblets/forms/widgets.py)
</details>

# Djblets Overview and Module Architecture

## Purpose and Scope

Djblets is a reusable Django utility library that packages shared infrastructure for Django-based applications, most notably Review Board. Rather than providing a single integrated framework, Djblets is composed of independent modules—caching, data grids, extensions, configuration forms, web APIs, asset pipelines, mail, and site configuration—each of which a downstream project can adopt without coupling to the rest. The package is published on PyPI as `Djblets` and installed via `pip3 install Djblets` (Source: [README.rst]()). Releases follow semantic versioning; Djblets 6 is the current major line and includes substantial improvements across nearly every module (Source: [README.rst]()).

## High-Level Module Organization

Djblets is laid out as a tree of Django apps under the top-level `djblets/` package. Each submodule ships its own `__init__.py`, `models.py`, `urls.py`, `templates/`, and `migrations/`, so a consumer can register only the apps it actually needs in `INSTALLED_APPS`.

| Module | Responsibility |
| --- | --- |
| `djblets.cache` | Caching helpers, safe key construction, cache locks |
| `djblets.datagrid` | Tabular list views with sorting, filtering, paging |
| `djblets.extensions` | Extension discovery, registration, and lifecycle |
| `djblets.configforms` | Forms-driven admin-style configuration UIs |
| `djblets.forms.widgets` | Specialized widgets (e.g. `RelatedObjectWidget`) |
| `djblets.pipeline` | Asset pipeline integration (Rollup.js, UglifyJS) |
| `djblets.webapi` | Web API decorators, resources, error responses |
| `djblets.siteconfig` | Runtime site configuration store |
| `djblets.mail` | E-mail composition and delivery helpers |

Source: [djblets/__init__.py](), [djblets/cache/__init__.py](), [djblets/datagrid/__init__.py](), [djblets/extensions/__init__.py](), [djblets/configforms/__init__.py](), [djblets/pipeline/__init__.py](), [djblets/webapi/__init__.py](), [djblets/siteconfig/__init__.py](), [djblets/mail/__init__.py]()

## Cross-Cutting Concerns

### Dependency Management

Djblets declares its Python runtime requirements through `pyproject.toml`, pinning versions of Django and supporting libraries that each release is validated against (Source: [pyproject.toml]()). Runtime compatibility checks and helper utilities live in `djblets/dependencies.py`, which exposes feature flags consumed by other modules to adapt to available dependencies (Source: [djblets/dependencies.py]()).

### Versioning

The package version is centralized in `djblets/_version.py` and re-exported through `djblets/__init__.py`, allowing applications to introspect the installed Djblets release at runtime (Source: [djblets/_version.py](), [djblets/__init__.py]()).

### Extension Framework

The `djblets.extensions` module provides a thread-safe registry used to discover, enable, and disable third-party extensions. Thread-safe registries were introduced in Djblets 5.0 (Source: [djblets/extensions/__init__

---

<a id='page-2'></a>

## Extension Framework: Writing and Testing Extensions

### Related Pages

Related topics: [Djblets Overview and Module Architecture](#page-1), [Caching: Backends, Keys, Locks, and Synchronization](#page-3), [WebAPI Resources, Datagrids, Forms, and Registries](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [djblets/extensions/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/__init__.py)
- [djblets/extensions/extension.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/extension.py)
- [djblets/extensions/hooks.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/hooks.py)
- [djblets/extensions/manager.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/manager.py)
- [djblets/extensions/models.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/models.py)
- [djblets/extensions/registry.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/registry.py)
- [djblets/extensions/settings.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/settings.py)
- [djblets/extensions/testcases.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/testcases.py)
- [djblets/extensions/packaging/setuptools_backend.py](https://github.com/djblets/djblets/blob/main/djblets/extensions/packaging/setuptools_backend.py)
</details>

# Extension Framework: Writing and Testing Extensions

The Djblets extension framework provides a runtime plugin layer that lets third-party Python packages integrate with a Django-based host application (most notably Review Board) without requiring changes to host code. Extensions can register hooks, contribute URL patterns, ship their own database models, expose configuration pages, and bundle static media. The framework defines a stable contract — `Extension`, `Hook`, settings, models, packaging, and test helpers — that extension authors target.

## Writing an Extension

### The `Extension` base class

Every extension is a Python package that defines a subclass of `djblets.extensions.extension.Extension`. The class declares metadata (`metadata`, `default_settings`) and a lifecycle (`initialize`, `shutdown`) that the host calls at enable/disable time. Source: [djblets/extensions/extension.py:1-150]()

Key author-facing attributes:

| Attribute / Method           | Purpose                                                                                              |
|------------------------------|------------------------------------------------------------------------------------------------------|
| `metadata`                   | `ExtensionInfo` declaring `name`, `version`, `author`, `license`, `summary`, `description`, `url`.    |
| `default_settings`           | Per-extension settings dictionary; merged into the host's site configuration on enable.              |
| `is_installed()`             | Class method indicating whether the extension package is installed in the environment.              |
| `initialize()`               | Called once after enable; override to register hooks, URLs, models, or schema work.                  |
| `shutdown()`                 | Called before disable; mirror of `initialize()` for resource cleanup.                                |
| `get_urlpatterns()`          | Returns additional URL patterns to mount under the host's URL conf.                                   |
| `get_static_urls()`          | Returns static file paths to merge into the host's staticfiles discovery.                           |
| `get_media_urls()`           | Returns URLs served by the extension at runtime (e.g., for upload media).                            |

Additional helpers on `Extension` cover extending the navigation bar, registering admin links, and accessing the active site config from `get_siteconfig()`. Source: [djblets/extensions/extension.py:150-400]()

### Hooks

Extensions respond to host-emitted events by subclassing hook classes from `djblets.extensions.hooks`. Hooks (e.g., `URLHook`, `TemplateHook`, `SignalHook`, `ActionHook`, `DataHook`) encapsulate a single integration point with a documented method signature. An extension registers a hook by instantiating it in `initialize()`, passing any required scope. Source: [djblets/extensions/hooks.py:1-150]()

The framework then owns the registry of hooks and dispatches host events through a `HookManager`. For example, a `URLHook` advertises extra URL patterns, while a `TemplateHook` injects rendered HTML at a named `template_hooks` location. Hook lifetime is bound to the owning `Extension`, so disabling the extension tears the hooks down automatically.

### Settings, Models, URLs, and Static Media

- **Settings** — `Extension.default_settings` is persisted through the extension's settings model and surfaced via `Extension.settings`, which the framework resolves through the registered manager. Source: [djblets/extensions/settings.py:1-120]()
- **Models** — Extensions can ship Django models by placing them in a `models.py` inside the extension package. The `InstalledExtension` and `Extension` settings models persist enable/disable state and per-extension configuration. Source: [djblets/extensions/models.py:1-200]()
- **URL patterns** — `get_urlpatterns()` returns Django `url()`/`path()` entries that the host mounts under a configurable prefix. Source: [djblets/extensions/extension.py:200-260]()
- **Static files** — `get_static_urls()` and per-package `static/` directories are merged at collectstatic time. The 5.2.2 release specifically addressed a race condition when installing static media for extensions, making this path more reliable. Source: [djblets/extensions/__init__.py:1-80]()

### Discovery, Registration, and Packaging

`ExtensionManager` (in `djblets/extensions/manager.py`) is the runtime component that finds installed extension packages, builds `Extension` instances, persists state via the `InstalledExtension` model, and exposes enable/disable APIs. Source: [djblets/extensions/manager.py:1-200]()

A `registry` singleton keeps the canonical in-memory map of `extension_id → Extension`. Source: [djblets/extensions/registry.py:1-80]()

For distribution, modern extensions are packaged with Djblets' `setuptools` backend, which advertises the package as an "Extension Distribution" (`ExtensionDistribution`) carrying `metadata` for the host to consume. Source: [djblets/extensions/packaging/setuptools_backend.py:1-150]()

## Testing an Extensions

Djblets provides a test harness purpose-built for extensions in `djblets/extensions/testcases.py`. The main entry points are:

```mermaid
flowchart LR
  TC[ExtensionTestCase] --> EU[extension.enable]
  EU --> EH[initialize hooks]
  EH --> TR[runTest body]
  TR --> XD[extension.disable]
  XD --> SH[shutdown hooks]
```

- `ExtensionTestCase` is a `unittest.TestCase` subclass that activates a target extension, runs the test body, and deactivates the extension in `tearDown` regardless of failure. This guarantees `shutdown()` runs and temporary state is not leaked across tests. Source: [djblets/extensions/testcases.py:1-160]()
- Helpers exist for testing rendering, form submission against extension URLs, and siteconfig changes applied by the extension.
- Because `ExtensionTestCase` triggers the full `initialize → shutdown` cycle, it also exercises the hook registration path; combined with `manage.py test`, this gives coverage from "extension is installed" through "host calls into the hook."

## Architecture in Brief

The lifecycle can be summarized as:

1. **Discovery** — `ExtensionManager` enumerates installed extension distributions.
2. **Loading** — Each distribution's `Extension` subclass is imported and wrapped by `registry`.
3. **Enable** — `InstalledExtension.enabled=True` triggers `Extension.initialize()`, which registers hooks, URLs, models, and static media.
4. **Operation** — The host fires events that flow through `hooks.py` into the registered hook instances.
5. **Disable** — `shutdown()` runs in reverse, hooks detach, models and URLs disappear until the extension is re-enabled. Source: [djblets/extensions/manager.py:200-400](), [djblets/extensions/extension.py:400-500]()

Together, the `Extension` class, hook primitives, `ExtensionManager`, models, packaging backend, and `ExtensionTestCase` form a complete authoring environment — the same surface Djblets itself uses for its bundled extensions.

---

<a id='page-3'></a>

## Caching: Backends, Keys, Locks, and Synchronization

### Related Pages

Related topics: [Djblets Overview and Module Architecture](#page-1), [Extension Framework: Writing and Testing Extensions](#page-2), [WebAPI Resources, Datagrids, Forms, and Registries](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [djblets/cache/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/cache/__init__.py)
- [djblets/cache/backend.py](https://github.com/djblets/djblets/blob/main/djblets/cache/backend.py)
- [djblets/cache/backend_compat.py](https://github.com/djblets/djblets/blob/main/djblets/cache/backend_compat.py)
- [djblets/cache/forwarding_backend.py](https://github.com/djblets/djblets/blob/main/djblets/cache/forwarding_backend.py)
- [djblets/cache/serials.py](https://github.com/djblets/djblets/blob/main/djblets/cache/serials.py)
- [djblets/cache/synchronizer.py](https://github.com/djblets/djblets/blob/main/djblets/cache/synchronizer.py)
</details>

# Caching: Backends, Keys, Locks, and Synchronization

The `djblets.cache` package provides a unified abstraction layer over Django's cache framework, designed to harden production deployments against cache poisoning, support distributed locking, and enable inter-process state synchronization. It was substantially redesigned in Djblets 6 with safer key construction and lock primitives. Source: [djblets/cache/__init__.py:1-50]()

## Cache Backends

The package exposes a `CacheBackend` class that wraps an underlying Django-compatible cache (e.g., `memcached`, `locmem`, `redis`, or `database`) and adds Djblets-specific behavior. Key responsibilities include:

- Normalizing access to the configured backend through Django's `CACHES` setting.
- Encapsulating a consistent API across backends that may differ in feature support (atomic operations, TTLs, key length limits).
- Providing a `ForwardingBackend` helper that proxies calls to a designated backend, allowing per-instance overrides.

```python
from djblets.cache import cache
cache.set("user:1:profile", {"name": "Alice"}, timeout=300)
profile = cache.get("user:1:profile")
```

The compatibility layer (`backend_compat.py`) detects which backend is in use and adapts unsupported operations (e.g., `incr`, `add`) into equivalent portable calls. Source: [djblets/cache/backend_compat.py:1-80]()

The `forwarding_backend.py` module allows constructing a cache that delegates to another backend instance, useful for testing and for multi-tier cache strategies. Source: [djblets/cache/forwarding_backend.py:1-60]()

## Cache Key Generation

A primary focus of Djblets 6 was eliminating **cache key poisoning**. The `make_key` / `make_cache_key` helpers construct deterministic, length-safe, and case-stable keys:

| Concern | Mitigation |
| --- | --- |
| Whitespace and control characters in keys | Stripped or hashed |
| Version collision across apps | Version/scope prefixes |
| Backend key-length limits | Hash truncation for memcached |
| Case sensitivity | Normalized lowercase |

```mermaid
flowchart LR
    A[Caller passes key parts] --> B[Normalize & validate]
    B --> C[Apply scope prefix]
    C --> D[Hash if exceeds length limit]
    D --> E[Return safe cache key]
```

`make_key` accepts a key, an optional `version`, and a `prefix` to namespace keys. Source: [djblets/cache/backend.py:120-180]()

A typical usage pattern is:

```python
key = cache.make_key("review_request:%s:state" % obj_id)
```

For backward compatibility, keys generated by older Djblets versions continue to be readable, but new writes always use the hardened format.

## Cache Locks

Djblets 6 introduces **cache lock support**, enabling short-lived distributed mutexes backed by the same cache store. The lock API typically includes:

- `cache.lock(key, timeout=...)` returning a context manager or similar object.
- Non-blocking acquisition via `try_lock`.
- Automatic release on expiry or explicit `unlock`.

```python
with cache.lock("refresh:repo:%s" % repo_id, timeout=10):
    do_expensive_refresh(repo_id)
```

Locks rely on atomic `add`-style operations where supported, falling back to TTL-based locking when the backend cannot offer true atomicity. Source: [djblets/cache/backend.py:200-260]()

This primitive is intended for low-contention critical sections such as rate-limit token resets, single-flight computation, and resource invalidations.

## Synchronization and Serials

For coordinating state across workers, `djblets.cache.synchronizer` provides a higher-level construct that watches versioned values ("serials") and invalidates cached state when they change.

The `serials.py` module defines helpers to generate monotonically increasing version identifiers used as part of cache keys. When the serial is bumped, dependent caches are considered stale.

```mermaid
sequenceDiagram
    participant W1 as Worker 1
    participant Cache
    participant W2 as Worker 2
    W1->>Cache: get_serial("foo")
    W2->>Cache: bump_serial("foo")
    W2->>Cache: set("foo:v2:data", new_value)
    W1->>Cache: get("foo:v1:data")
    Cache-->>W1: stale or None
```

The `synchronizer.py` module orchestrates the relationship between user-visible cache keys and their serial counterparts, providing `sync() / clear() / update()` style methods. Source: [djblets/cache/synchronizer.py:1-120]()

This design supports scenarios such as:

- Broadcasting configuration changes without restarting workers.
- Coordinating database query caches across multiple front-end processes.
- Implementing eventual-consistency patterns for review-board-like workloads that Djblets targets.

## Summary

| Component | Purpose |
| --- | --- |
| `backend.py` | Core `CacheBackend`, key generation, locks |
| `backend_compat.py` | Feature detection and fallback shims |
| `forwarding_backend.py` | Backend delegation and test seams |
| `serials.py` | Versioned identifiers for invalidation |
| `synchronizer.py` | Cross-process cache coordination |

Together these modules transform Django's cache API into a production-grade substrate for distributed Djblets applications. Source: [djblets/cache/__init__.py:1-50]()

For upgrade guidance, note the breaking changes in Djblets 6 documented in the [release notes](https://github.com/djblets/djblets/releases/tag/release-6.0), particularly around cache key formats and the new lock primitives introduced in this module.

---

<a id='page-4'></a>

## WebAPI Resources, Datagrids, Forms, and Registries

### Related Pages

Related topics: [Djblets Overview and Module Architecture](#page-1), [Extension Framework: Writing and Testing Extensions](#page-2), [Caching: Backends, Keys, Locks, and Synchronization](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [djblets/webapi/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/webapi/__init__.py)
- [djblets/webapi/resources/base.py](https://github.com/djblets/djblets/blob/main/djblets/webapi/resources/base.py)
- [djblets/webapi/resources/registry.py](https://github.com/djblets/djblets/blob/main/djblets/webapi/resources/registry.py)
- [djblets/webapi/decorators.py](https://github.com/djblets/djblets/blob/main/djblets/webapi/decorators.py)
- [djblets/datagrid/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/datagrid/__init__.py)
- [djblets/datagrid/grids.py](https://github.com/djblets/djblets/blob/main/djblets/datagrid/grids.py)
- [djblets/forms/__init__.py](https://github.com/djblets/djblets/blob/main/djblets/forms/__init__.py)
- [djblets/forms/widgets.py](https://github.com/djblets/djblets/blob/main/djblets/forms/widgets.py)
- [djblets/configforms/forms.py](https://github.com/djblets/djblets/blob/main/djblets/configforms/forms.py)
- [djblets/registry/registry.py](https://github.com/djblets/djblets/blob/main/djblets/registry/registry.py)
</details>

# WebAPI Resources, Datagrids, Forms, and Registries

## Overview

Djblets groups several large feature areas that, together, let a Django project ship a full review-style product: a resource-oriented WebAPI framework, a configurable data grid UI, layered form and configform abstractions, and a thread-safe registry system. These modules are intentionally decoupled — a datagrid can render any queryset, a WebAPI resource can serialize any Django model, a configform can manage any settings page, and the registry plumbs them together so extensions can register new behavior at runtime. As described in the Djblets 6 release notes, this release brought substantial improvements to "nearly all module[s]," including the datagrid, webapi, forms, and registry subsystems.

## WebAPI Resources

The `djblets.webapi` package is a resource-oriented HTTP layer built on top of Django views `Source: [djblets/webapi/__init__.py:1-50]()`. The central class is `WebAPIResource`, declared in `djblets/webapi/resources/base.py`. Each resource declares metadata such as `name`, `uri_object_key`, `model`, allowed HTTP `methods`, a list of `subresources`, and serialization `fields` `Source: [djblets/webapi/resources/base.py:1-120]()`. HTTP methods are mapped to handler functions using helpers in `djblets/webapi/decorators.py`, including `@webapi_request_fields`, `@webapi_response_fields`, and `@augment_method_from`, the last of which lets one HTTP method inherit and extend the behavior of another `Source: [djblets/webapi/decorators.py:1-120]()`.

Resources are exposed through a `ResourceList` in `djblets/webapi/resources/registry.py`, which is itself a registry. When a project includes `djblets.urls`, the URL conf walks this registry and wires each registered resource under its declared URI templates `Source: [djblets/webapi/resources/registry.py:1-100]()`. Convenience subclasses such as `RootResource`, `UserResource`, and `GroupResource` ship in `djblets/webapi/resources/` and serve as canonical examples for extension authors. Authentication is provided through `WebAPIAuthentication` subclasses and rate limiting through `WebAPIRateLimiter` subclasses; errors are normalized via `WebAPIError` and friends so the API returns structured JSON error payloads consistently.

## Datagrids

`djblets.datagrid` renders sortable, paginated, and customizable tabular views `Source: [djblets/datagrid/__init__.py:1-40]()`. The main class, `DataGrid`, lives in `djblets/datagrid/grids.py` and is initialized with a request and a base queryset `Source: [djblets/datagrid/grids.py:1-120]()`. Columns are instances of `Column` subclasses declared on the grid; Djblets ships built-ins such as `DateTimeColumn`, `LinkedColumn`, `StateColumn`, `ImageColumn`, and `CheckboxColumn` in `djblets/datagrid/columns.py`.

Columns can be marked as customizable, in which case the grid loads and saves each user's column preferences through the `datagridcolumns_prefs` registry-backed mechanism. The Djblets 5.0 release notes describe "faster datagrids," and Djblets 6 continues performance work in this area. JavaScript-side, the datagrid (`djblets/js/djblets/datagrid.js`) consumes the matching WebAPI resource to fetch, sort, and paginate rows over HTTP, which is why datagrids and WebAPI resources are almost always built as a pair.

## Forms and ConfigForms

`djblets.forms` extends Django's form framework with custom widgets and helpers `Source: [djblets/forms/__init__.py:1-60]()`. Notable widgets include `RelatedObjectWidget` in `djblets/forms/widgets.py`, a "scalable alternative for Django's filtered relation selector" that was patched in Djblets 5.2.1 to fix regressions introduced in 5.2 `Source: [djblets/forms/widgets.py:1-120]()`. Other widgets such as `MarkdownTextArea` are used wherever rich text appears in Review Board.

Above this sits `djblets.configforms`, which models a multi-section settings page. A `ConfigPage` glues together several `ConfigForm` subclasses, each contributing a section with its own fields and save handlers `Source: [djblets/configforms/forms.py:1-150]()`. ConfigForms are heavily used by extension settings UIs — Djblets 5.0.1 specifically fixed a regression in action handler registration for `djblets.configforms` that had broken Enable/Disable buttons for extensions in the admin UI. The Djblets 5.1.1 release notes also highlight improvements to configuration forms and e-mail handling, reinforcing this area as actively maintained.

## Registries

The `djblets.registry` package provides the `Registry` base class in `djblets/registry/registry.py` `Source: [djblets/registry/registry.py:1-120]()`. A registry maintains a mapping from registration keys (usually a string ID) to item classes, with explicit `register()`, `unregister()`, and `get_lookup()` APIs. Registries were made thread-safe in Djblets 5.0, so they can be populated at import time and mutated concurrently.

Registries are the connective tissue of the rest of Djblets. The major registries and the items they hold are summarized below:

| Registry | Holds | Used by |
| --- | --- | --- |
| `WebAPIResource.registry` | `WebAPIResource` subclasses | URL routing, API consumers |
| `Extension.registry` | `Extension` subclasses | Extension loading |
| `DataGridColumn.registry` | `Column` subclasses | Datagrid column selection |
| `FormWidget.registry`-style maps | Widget classes | Forms and configforms |

Typical usage is to declare a subclass and let a decorator — or an explicit `register()` call on the host registry — make it available:

```python
class MyResource(WebAPIResource):
    name = 'my-resource'
    ...
```

Extensions use the same pattern via `Extension.registry`, ensuring that third-party code can plug into the host application declaratively rather than through hard-coded imports. This registry-driven design is what enables Djblets' extension ecosystem and is the reason almost every feature area in this page ultimately depends on `djblets.registry`.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: djblets/djblets

Summary: Found 6 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Capability evidence risk - Capability evidence risk requires verification.

## 1. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/djblets/djblets

## 2. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/djblets/djblets

## 3. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/djblets/djblets

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/djblets/djblets

## 5. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/djblets/djblets

## 6. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/djblets/djblets

<!-- canonical_name: djblets/djblets; human_manual_source: deepwiki_human_wiki -->
