Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion base_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ django-timezone-field

# A REST API framework for Django projects
# https://www.django-rest-framework.org/community/release-notes/
djangorestframework
# TODO: Re-evaluate the monkey-patch of get_unique_validators() before upgrading
djangorestframework==3.16.1

# Sane and flexible OpenAPI 3 schema generation for Django REST framework.
# https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst
Expand Down
39 changes: 39 additions & 0 deletions netbox/netbox/monkey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.db.models import UniqueConstraint
from rest_framework.utils.field_mapping import get_unique_error_message
from rest_framework.validators import UniqueValidator

__all__ = (
'get_unique_validators',
)


def get_unique_validators(field_name, model_field):
"""
Extend Django REST Framework's get_unique_validators() function to attach a UniqueValidator to a field *only* if the
associated UniqueConstraint does NOT have a condition which references another field. See bug #19302.
"""
field_set = {field_name}
conditions = {
c.condition
for c in model_field.model._meta.constraints
if isinstance(c, UniqueConstraint) and set(c.fields) == field_set
}

# START custom logic
conditions = {
cond for cond in conditions
if cond.referenced_base_fields == field_set
}
# END custom logic

if getattr(model_field, 'unique', False):
conditions.add(None)
if not conditions:
return
unique_error_message = get_unique_error_message(model_field)
queryset = model_field.model._default_manager
for condition in conditions:
yield UniqueValidator(
queryset=queryset if condition is None else queryset.filter(condition),
message=unique_error_message
)
12 changes: 12 additions & 0 deletions netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.core.validators import URLValidator
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
from rest_framework.utils import field_mapping

from core.exceptions import IncompatiblePluginError
from netbox.config import PARAMS as CONFIG_PARAMS
Expand All @@ -20,6 +21,17 @@
import storages.utils # type: ignore
from utilities.release import load_release_data
from utilities.string import trailing_slash
from .monkey import get_unique_validators


#
# Monkey-patching
#

# TODO: Remove this once #20547 has been implemented
# Override DRF's get_unique_validators() function with our own (see bug #19302)
field_mapping.get_unique_validators = get_unique_validators


#
# Environment setup
Expand Down