• Get Review Board
  • What's New
  • Products
  • Review Board Code review, image review, and document review
  • Documentation
  • Release Notes
  • Power Pack Enterprise integrations, reports, and enhanced document review
  • Try for 60 Days
  • Purchase
  • RBCommons Review Board as a Service, hosted by us
  • Pricing
  • RBTools Command line tools and Python API for Review Board
  • Documentation
  • Release Notes
  • Review Bot Automated code review, connecting tools you already use
  • Documentation
  • Release Notes
  • RB Gateway Manage Git and Mercurial repositories in your network
  • Documentation
  • Release Notes
  • Learn and Explore
  • What is Code Review?
  • Documentation
  • Frequently Asked Questions
  • Support Options
  • Third-Party Integrations
  • Demo
  • Review Board RBTools Power Pack Review Bot Djblets RB Gateway
    1. Djblets 5.x
    2. Version 5.x
    3. Version 4.x
    4. Version 3.x
    5. Version 2.x
    6. Version 2.0
    7. Version 1.0
    8. Version 0.9
    9. Djblets Documentation
    10. Guides
    11. Registries Guides
    12. Writing Registries
  • Home
  • Guides
  • Avatar Services Guides
  • Writing Avatar Services
  • Extension Guides
  • Writing Extensions
  • Testing Extensions
  • Feature Checks Guides
  • Introduction to Feature Checks
  • Writing Features
  • Writing Feature Checkers
  • Testing with Feature Checks
  • Integration Guides
  • Supporting Integrations
  • Writing Integrations
  • Privacy Compliance Guides
  • Getting and Checking Consent
  • Working with Personally Identifiable Information
  • Service Integrations
  • reCAPTCHA Guides
  • Using reCAPTCHA
  • Registries Guides
  • Writing Registries
  • Web API Guides
  • Writing Web API Resources
  • Adding OAuth2 Support
  • Module and Class References
  • djblets
  • djblets.deprecation
  • djblets.auth.forms
  • djblets.auth.ratelimit
  • djblets.auth.signals
  • djblets.auth.util
  • djblets.auth.views
  • djblets.avatars.errors
  • djblets.avatars.forms
  • djblets.avatars.registry
  • djblets.avatars.services
  • djblets.avatars.services.base
  • djblets.avatars.services.fallback
  • djblets.avatars.services.file_upload
  • djblets.avatars.services.gravatar
  • djblets.avatars.services.url
  • djblets.avatars.settings
  • djblets.cache.backend
  • djblets.cache.backend_compat
  • djblets.cache.context_processors
  • djblets.cache.errors
  • djblets.cache.forwarding_backend
  • djblets.cache.serials
  • djblets.cache.synchronizer
  • djblets.conditions
  • djblets.conditions.choices
  • djblets.conditions.conditions
  • djblets.conditions.errors
  • djblets.conditions.operators
  • djblets.conditions.values
  • djblets.configforms.forms
  • djblets.configforms.mixins
  • djblets.configforms.pages
  • djblets.configforms.registry
  • djblets.configforms.views
  • djblets.datagrid.grids
  • djblets.db.backends.mysql.base
  • djblets.db.fields
  • djblets.db.fields.base64_field
  • djblets.db.fields.comma_separated_values_field
  • djblets.db.fields.counter_field
  • djblets.db.fields.json_field
  • djblets.db.fields.modification_timestamp_field
  • djblets.db.fields.relation_counter_field
  • djblets.db.managers
  • djblets.db.query
  • djblets.db.query_catcher
  • djblets.db.query_comparator
  • djblets.db.validators
  • djblets.extensions.admin
  • djblets.extensions.errors
  • djblets.extensions.extension
  • djblets.extensions.forms
  • djblets.extensions.hooks
  • djblets.extensions.loaders
  • djblets.extensions.manager
  • djblets.extensions.middleware
  • djblets.extensions.models
  • djblets.extensions.packaging
  • djblets.extensions.resources
  • djblets.extensions.settings
  • djblets.extensions.signals
  • djblets.extensions.staticfiles
  • djblets.extensions.testing
  • djblets.extensions.testing.testcases
  • djblets.extensions.urls
  • djblets.extensions.views
  • djblets.extensions.templatetags.djblets_extensions
  • djblets.features
  • djblets.features.checkers
  • djblets.features.decorators
  • djblets.features.errors
  • djblets.features.feature
  • djblets.features.level
  • djblets.features.registry
  • djblets.features.testing
  • djblets.features.templatetags.features
  • djblets.forms.fields
  • djblets.forms.fieldsets
  • djblets.forms.forms
  • djblets.forms.forms.key_value_form
  • djblets.forms.widgets
  • djblets.gravatars
  • djblets.gravatars.templatetags.gravatars
  • djblets.http.middleware
  • djblets.http.responses
  • djblets.integrations.errors
  • djblets.integrations.forms
  • djblets.integrations.hooks
  • djblets.integrations.integration
  • djblets.integrations.manager
  • djblets.integrations.mixins
  • djblets.integrations.models
  • djblets.integrations.urls
  • djblets.integrations.views
  • djblets.log
  • djblets.log.middleware
  • djblets.log.siteconfig
  • djblets.log.urls
  • djblets.log.views
  • djblets.mail.dmarc
  • djblets.mail.message
  • djblets.mail.testing
  • djblets.mail.utils
  • djblets.markdown
  • djblets.markdown.extensions.escape_html
  • djblets.markdown.extensions.wysiwyg
  • djblets.markdown.extensions.wysiwyg_email
  • djblets.pipeline.compilers.es6
  • djblets.pipeline.compilers.less
  • djblets.pipeline.compilers.mixins
  • djblets.pipeline.compilers.rollup
  • djblets.pipeline.compilers.typescript
  • djblets.pipeline.settings
  • djblets.privacy.consent
  • djblets.privacy.consent.base
  • djblets.privacy.consent.common
  • djblets.privacy.consent.errors
  • djblets.privacy.consent.forms
  • djblets.privacy.consent.hooks
  • djblets.privacy.consent.registry
  • djblets.privacy.consent.tracker
  • djblets.privacy.models
  • djblets.privacy.pii
  • djblets.privacy.templatetags.djblets_privacy
  • djblets.recaptcha.mixins
  • djblets.recaptcha.siteconfig
  • djblets.recaptcha.templatetags.djblets_recaptcha
  • djblets.recaptcha.widgets
  • djblets.registries
  • djblets.registries.errors
  • djblets.registries.importer
  • djblets.registries.mixins
  • djblets.registries.registry
  • djblets.registries.signals
  • djblets.secrets
  • djblets.secrets.crypto
  • djblets.secrets.token_generators
  • djblets.secrets.token_generators.base
  • djblets.secrets.token_generators.legacy_sha1
  • djblets.secrets.token_generators.registry
  • djblets.secrets.token_generators.vendor_checksum
  • djblets.siteconfig
  • djblets.siteconfig.admin
  • djblets.siteconfig.context_processors
  • djblets.siteconfig.django_settings
  • djblets.siteconfig.forms
  • djblets.siteconfig.managers
  • djblets.siteconfig.middleware
  • djblets.siteconfig.models
  • djblets.siteconfig.signals
  • djblets.siteconfig.views
  • djblets.template.caches
  • djblets.template.context
  • djblets.template.loaders.conditional_cached
  • djblets.template.loaders.namespaced_app_dirs
  • djblets.testing.decorators
  • djblets.testing.testcases
  • djblets.testing.testrunners
  • djblets.urls.context_processors
  • djblets.urls.decorators
  • djblets.urls.patterns
  • djblets.urls.resolvers
  • djblets.urls.root
  • djblets.urls.staticfiles
  • djblets.util.compat.django.core.cache
  • djblets.util.compat.python.past
  • djblets.util.contextmanagers
  • djblets.util.dates
  • djblets.util.decorators
  • djblets.util.filesystem
  • djblets.util.functional
  • djblets.util.html
  • djblets.util.http
  • djblets.util.humanize
  • djblets.util.json_utils
  • djblets.util.properties
  • djblets.util.serializers
  • djblets.util.symbols
  • djblets.util.templatetags.djblets_deco
  • djblets.util.templatetags.djblets_email
  • djblets.util.templatetags.djblets_forms
  • djblets.util.templatetags.djblets_images
  • djblets.util.templatetags.djblets_js
  • djblets.util.templatetags.djblets_utils
  • djblets.util.typing
  • djblets.util.views
  • djblets.views.generic.base
  • djblets.views.generic.etag
  • djblets.webapi.auth
  • djblets.webapi.auth.backends
  • djblets.webapi.auth.backends.api_tokens
  • djblets.webapi.auth.backends.base
  • djblets.webapi.auth.backends.basic
  • djblets.webapi.auth.backends.oauth2_tokens
  • djblets.webapi.auth.views
  • djblets.webapi.decorators
  • djblets.webapi.encoders
  • djblets.webapi.errors
  • djblets.webapi.fields
  • djblets.webapi.managers
  • djblets.webapi.models
  • djblets.webapi.oauth2_scopes
  • djblets.webapi.resources
  • djblets.webapi.resources.base
  • djblets.webapi.resources.group
  • djblets.webapi.resources.registry
  • djblets.webapi.resources.root
  • djblets.webapi.resources.user
  • djblets.webapi.resources.mixins.api_tokens
  • djblets.webapi.resources.mixins.forms
  • djblets.webapi.resources.mixins.oauth2_tokens
  • djblets.webapi.resources.mixins.queries
  • djblets.webapi.responses
  • djblets.webapi.signals
  • djblets.webapi.testing
  • djblets.webapi.testing.decorators
  • djblets.webapi.testing.testcases
  • General Index
  • Python Module Index
  • Release Notes
  • Writing Registries¶

    Overview¶

    Registries are utilities for keeping track of objects. They guarantee that each element registered with them is unique and do not share any attributes with any other registered elements. That is, for each attribute the registry defines, no two registered elements will have the same value for that attribute.

    Subclassing Registries¶

    Registries are intended to be subclassed.

    In Djblets 3.1 and higher, subclasses can specify the type of item managed by the registry when subclassing. If not specified, any values are allowed. This is specified by doing:

    class MyRegistry(Registry[Item]):
        ...
    

    Registries have attributes that subclasses should override to customize the behaviour:

    • lookup_attrs, which determine which attributes on the elements will be usable as lookup attributes. This should be either a tuple or a list containing strings of attribute names.

      For example:

      class MyRegistry(Registry[Item]):
          lookup_attrs = ['id', 'name']
      
      registry = MyRegistry()
      
      # Register a new item.
      registry.register(Item(id=0, name='bar'))
      
      # Look up the item by its attributes.
      assert registry.get('id', 0) is registry.get('name', 'bar')
      
      # Unregister the item.
      registry.unregister_by('id', 0)
      
    • errors, which determines the error interpolation strings for exceptions raised by the registry. This allows registry subclasses to customized and contextualized error messages about the type of item in the registry, instead of referring to “item”s.

      These messages override the default error messages, which are defined in the DEFAULT_ERRORS dictionary.

    • lookup_error_class, which determines the exception class for item lookup errors (i.e., when an item cannot be found in the registry). This should be a subclass of ItemLookupError.

    Overriding Error Messages¶

    The error messages provided by registries are intentionally vague. To give more specific error messages, the errors attribute can be overridden. This attribute provides the error interpolation strings for errors in the registry.

    For example:

    from django.utils.translation import gettext as _
    from djblets.registries.registry import ALREADY_REGISTERED, Registry
    
    
    class FooRegistry(Registry[Item]):
         errors = {
              ALREADY_REGISTERED: _(
                 'Could not register the foo "%(attr_name)": it is already '
                 'registered.',
              ),
         }
    

    If a subclass wishes to provide more default errors, the default_errors attribute can be overridden for this purpose.

    For example:

    from django.utils.translation import gettext as _
    from djblets.registries.errors import DEFAULT_ERRORS
    
    
    HTTP_ERROR = 'http_error'
    
    _DEFAULT_ERRORS = DEFAULT_ERRORS.copy()
    _DEFAULT_ERRORS.update({
        HTTP_ERROR: _(
            'There was an HTTP error: %(error)s.',
        ),
    })
    
    
    class ApiRegistry(Registry[Item]):
        """A registry that persists itself to an API."""
    
        default_errors = _DEFAULT_ERRORS
        api_url = "http://example.com"
    
        def save(self) -> None:
            try:
                update(api_url, list(self))
            except HttpError as e:
                raise Exception(self.format_error(HTTP_ERROR,
                                                  error=e))
    

    Default Item Registration¶

    Registries can provide default items to be registered when they are first accessed. These default items will populate the registry whenever one of the following methods is called:

    • get()

    • get_or_none()

    • register()

    • unregister()

    • unregister_by_attr()

    • populate()

    The registry will not be populated more than once. They are lazily populated and will never be populated until one of the above methods is called.

    For example:

    from typing import Iterable
    
    from djblets.registries.registry import Registry
    
    
    class DefaultItemsRegistry(Registry[int]):
        """A registry that provides default items."""
    
        def get_defaults(self) -> Iterable[int]:
            return [1, 2, 3]
    

    The get_defaults() method can either return an iterable (such as a list) or yield its items, as the result will only ever be consumed once

    Example Registries¶

    The following examples are practical uses of registries that may be useful beyond the default definition.

    Ordered Registries¶

    Suppose we wanted to retrieve each item from the registry in the order it was registered in. We can do that by keeping a list that contains the id() of each registered item. Then, instead of iterating through the registry in the default order, we can iterate through in the order the items were registered.

    class OrderedRegistry(Registry[Item]):
        """A registry which maintains the order of its items."""
    
        def __init__(self) -> None:
            self._key_order = []
            self._by_id = {}
            super().__init__()
    
        def register(
            self,
            item: Item,
        ) -> None:
            """Register an item and keep track of its insertion order."""
            super(OrderedRegistry, self).register(item)
            self._key_order.append(id(item))
            self._by_id[id(item)] = item
    
        def unregister(
            self,
            item: Item,
        ) -> None:
            """Unregister an item and remove it from the insertion order."""
            super(OrderedRegistry, self).unregister(item)
            key = id(item)
            del self._by_id[key]
            self._key_order.remove(key)
    
        def __iter__(self) -> Iterator[Item]:
            """Yield each registered item in insertion order."""
            for key in self._key_order:
                yield self._by_id[key]
    

    This behavior is available in the OrderedRegistry class.

    Exception-less Registries¶

    Deprecated since version 3.1: While still usable, it’s recommended that callers call get_or_none() instead. This works better with type annotations and helps with consistency and readability.

    If get() raising an exception is not useful and instead you would prefer a sentinel value (e.g., None) to be returned instead, the get() method could be overridden as in the following example.

    class SafeRegistry(Registry[Item]):
        """A registry that does not throw exceptions on item lookup failure."""
    
        def get(
            self,
            attr_name: str,
            attr_value: object,
        ) -> Optional[Item]:
            """Return the item if it is registered; otherwise, return None."""
            try:
                return super(SafeRegistry, self).get(attr_name, attr_value)
            except ItemLookupError:
                return None
    

    This behavior is also available as a mixin, as ExceptionFreeGetterMixin. It can be used as follows and is equivalent to the above code example.

    from djblets.registries.mixins import ExceptionFreeGetterMixin
    
    class SafeRegistry(ExceptionFreeGetterMixin[Item], Registry[Item]):
        pass
    

    Keep up with the latest Review Board releases, security updates, and helpful information.

    About
    News
    Demo
    RBCommons Hosting
    Integrations
    Happy Users
    Support Options
    Documentation
    FAQ
    User Manual
    RBTools
    Administration Guide
    Power Pack
    Release Notes
    Downloads
    Review Board
    RBTools
    Djblets
    Power Pack
    Package Store
    PGP Signatures
    Contributing
    Bug Tracker
    Submit Patches
    Development Setup
    Wiki
    Follow Us
    Mailing Lists
    Reddit
    Twitter
    Mastodon
    Facebook
    YouTube

    Copyright © 2006-2025 Beanbag, Inc. All rights reserved.

    Terms of Service — Privacy Policy — AI Ethics Policy — Branding

    On this page

    • [Top]
    • Overview
    • Subclassing Registries
      • Overriding Error Messages
      • Default Item Registration
    • Example Registries
      • Ordered Registries
      • Exception-less Registries