Skip to content

Consolidate uniqueness constraints which reference nullable fields #20547

@jeremystretch

Description

@jeremystretch

Proposed Changes

There are several instances in NetBox where we define two UniqueConstraints to handle nullable fields: One to handle null values, and one to handle non-null values. The Region model is one example. The UniqueConstraints defined below ensure that the name field is unique to its parent, if one is assigned, or otherwise unique to regions having no parent:

class Meta:
    constraints = (
        # Has no effect if parent is NULL, because PostgreSQL considers NULL != NULL
        models.UniqueConstraint(
            fields=('parent', 'name'),
            name='%(app_label)s_%(class)s_parent_name'
        ),
        # Checks that name is unique if parent is NULL
        models.UniqueConstraint(
            fields=('name',),
            name='%(app_label)s_%(class)s_name',
            condition=Q(parent__isnull=True),
            violation_error_message=_("A top-level region with this name already exists.")
        ),
    ...
    )

PostgreSQL 15 introduced support for declaring NULLS NOT DISTINCT on unique indexes, allowing for the two above constraints to be condensed into one by setting nulls_distinct=False:

class Meta:
    constraints = (
        models.UniqueConstraint(
            fields=('parent', 'name'),
            name='%(app_label)s_%(class)s_parent_name',
            nulls_distinct=False,  # Assert that NULL == NULL
        ),
    ...
    )

NOTE: This is not supported on PostgreSQL versions earlier than 15.

Justification

This will reduce the overall number of unique constraints we have defined, and greatly simplify the logic for ensuring uniqueness where nullable fields must be considered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    netboxstatus: blockedAnother issue or external requirement is preventing implementationtype: housekeepingChanges to the application which do not directly impact the end user

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions