Devices > Edit: (get() returned more than one Device) when using multi custom permissions by tags #6131

Closed
opened 2025-12-29 19:37:07 +01:00 by adam · 4 comments
Owner

Originally created by @seros1521 on GitHub (Feb 22, 2022).

Originally assigned to: @seros1521 on GitHub.

NetBox version

v3.1.8 (netbox-docker)

Python version

3.9

Steps to Reproduce

  1. create 2 tags: "tag1" and "tag2"
  2. add both tags to any device
  3. add to test user permission to read devices
  4. create permission: name - can_edit_device_with_tag1, action - 'can_change', object types - 'dcim > devices', constraints: '{"tags__name":"tag1"}'
  5. create permission: name - can_edit_device_with_tag2, action - 'can_change', object types - 'dcim > devices', constraints: '{"tags__name":"tag2"}'
  6. assign both permissions to the test user directly or to the groups they belong to
  7. try to edit device

Expected Behavior

The form for editing the device will open.

Observed Behavior

Exception Type: MultipleObjectsReturned at /dcim/devices/1/edit/
Exception Value: get() returned more than one Device -- it returned 2!
Detail:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/dcim/devices/1/edit/

Django Version: 3.2.12
Python Version: 3.9.5
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'corsheaders',
 'debug_toolbar',
 'graphiql_debug_toolbar',
 'django_filters',
 'django_tables2',
 'django_prometheus',
 'graphene_django',
 'mptt',
 'rest_framework',
 'social_django',
 'taggit',
 'timezone_field',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'wireless',
 'django_rq',
 'drf_yasg']
Installed Middleware:
['graphiql_debug_toolbar.middleware.DebugToolbarMiddleware',
 'django_prometheus.middleware.PrometheusBeforeMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'netbox.middleware.ExceptionHandlingMiddleware',
 'netbox.middleware.RemoteUserMiddleware',
 'netbox.middleware.LoginRequiredMiddleware',
 'netbox.middleware.DynamicConfigMiddleware',
 'netbox.middleware.APIVersionMiddleware',
 'netbox.middleware.ObjectChangeMiddleware',
 'django_prometheus.middleware.PrometheusAfterMiddleware']



Traceback (most recent call last):
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/views/generic.py", line 322, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/utilities/views.py", line 93, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/views/generic.py", line 325, in get
    obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
  File "/opt/netbox/netbox/netbox/views/generic.py", line 302, in get_object
    obj = get_object_or_404(self.queryset, pk=kwargs['pk'])
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/shortcuts.py", line 76, in get_object_or_404
    return queryset.get(*args, **kwargs)
  File "/opt/netbox/venv/lib/python3.9/site-packages/django/db/models/query.py", line 439, in get
    raise self.model.MultipleObjectsReturned(

Exception Type: MultipleObjectsReturned at /dcim/devices/1/edit/
Exception Value: get() returned more than one Device -- it returned 2!
Originally created by @seros1521 on GitHub (Feb 22, 2022). Originally assigned to: @seros1521 on GitHub. ### NetBox version v3.1.8 (netbox-docker) ### Python version 3.9 ### Steps to Reproduce 1. create 2 tags: "tag1" and "tag2" 2. add both tags to any device 3. add to test user permission to read devices 4. create permission: name - can_edit_device_with_tag1, action - 'can_change', object types - 'dcim > devices', constraints: '{"tags__name":"tag1"}' 5. create permission: name - can_edit_device_with_tag2, action - 'can_change', object types - 'dcim > devices', constraints: '{"tags__name":"tag2"}' 6. assign both permissions to the test user directly or to the groups they belong to 7. try to edit device ### Expected Behavior The form for editing the device will open. ### Observed Behavior Exception Type: MultipleObjectsReturned at /dcim/devices/1/edit/ Exception Value: get() returned more than one Device -- it returned 2! Detail: ``` Environment: Request Method: GET Request URL: http://127.0.0.1:8000/dcim/devices/1/edit/ Django Version: 3.2.12 Python Version: 3.9.5 Installed Applications: ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'corsheaders', 'debug_toolbar', 'graphiql_debug_toolbar', 'django_filters', 'django_tables2', 'django_prometheus', 'graphene_django', 'mptt', 'rest_framework', 'social_django', 'taggit', 'timezone_field', 'circuits', 'dcim', 'ipam', 'extras', 'tenancy', 'users', 'utilities', 'virtualization', 'wireless', 'django_rq', 'drf_yasg'] Installed Middleware: ['graphiql_debug_toolbar.middleware.DebugToolbarMiddleware', 'django_prometheus.middleware.PrometheusBeforeMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'netbox.middleware.ExceptionHandlingMiddleware', 'netbox.middleware.RemoteUserMiddleware', 'netbox.middleware.LoginRequiredMiddleware', 'netbox.middleware.DynamicConfigMiddleware', 'netbox.middleware.APIVersionMiddleware', 'netbox.middleware.ObjectChangeMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware'] Traceback (most recent call last): File "/opt/netbox/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/opt/netbox/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/opt/netbox/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view return self.dispatch(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/views/generic.py", line 322, in dispatch return super().dispatch(request, *args, **kwargs) File "/opt/netbox/netbox/utilities/views.py", line 93, in dispatch return super().dispatch(request, *args, **kwargs) File "/opt/netbox/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch return handler(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/views/generic.py", line 325, in get obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs) File "/opt/netbox/netbox/netbox/views/generic.py", line 302, in get_object obj = get_object_or_404(self.queryset, pk=kwargs['pk']) File "/opt/netbox/venv/lib/python3.9/site-packages/django/shortcuts.py", line 76, in get_object_or_404 return queryset.get(*args, **kwargs) File "/opt/netbox/venv/lib/python3.9/site-packages/django/db/models/query.py", line 439, in get raise self.model.MultipleObjectsReturned( Exception Type: MultipleObjectsReturned at /dcim/devices/1/edit/ Exception Value: get() returned more than one Device -- it returned 2! ```
adam added the type: bugstatus: accepted labels 2025-12-29 19:37:07 +01:00
adam closed this issue 2025-12-29 19:37:07 +01:00
Author
Owner

@seros1521 commented on GitHub (Feb 22, 2022):

See also #8351

@seros1521 commented on GitHub (Feb 22, 2022): See also #8351
Author
Owner

@seros1521 commented on GitHub (Feb 22, 2022):

if added to the call
90ee689d5a/netbox/utilities/querysets.py (L42)
.distinct()
the error disappears, but I'm not sure if this is the right way to solve it and it won't break anything.

@seros1521 commented on GitHub (Feb 22, 2022): if added to the call https://github.com/netbox-community/netbox/blob/90ee689d5ae61e829042e35824b4ff4201476e44/netbox/utilities/querysets.py#L42 `.distinct()` the error disappears, but I'm not sure if this is the right way to solve it and it won't break anything.
Author
Owner

@jeremystretch commented on GitHub (Feb 22, 2022):

Seems like a bug with the tags filter:

>>> Site.objects.filter(tags__name='Golf').filter(tags__name='Lima').get(pk=24)
<Site: Butler Communications>
>>> Site.objects.filter(tags__name__in=['Golf', 'Lima']).get(pk=24)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/opt/netbox/venv/lib/python3.8/site-packages/django/db/models/query.py", line 439, in get
    raise self.model.MultipleObjectsReturned(
dcim.models.sites.Site.MultipleObjectsReturned: get() returned more than one Site -- it returned 2!
@jeremystretch commented on GitHub (Feb 22, 2022): Seems like a bug with the `tags` filter: ``` >>> Site.objects.filter(tags__name='Golf').filter(tags__name='Lima').get(pk=24) <Site: Butler Communications> >>> Site.objects.filter(tags__name__in=['Golf', 'Lima']).get(pk=24) Traceback (most recent call last): File "<console>", line 1, in <module> File "/opt/netbox/venv/lib/python3.8/site-packages/django/db/models/query.py", line 439, in get raise self.model.MultipleObjectsReturned( dcim.models.sites.Site.MultipleObjectsReturned: get() returned more than one Site -- it returned 2! ```
Author
Owner

@seros1521 commented on GitHub (Feb 28, 2022):

From: https://django-taggit.readthedocs.io/en/latest/api.html#filtering:

If you’re filtering on multiple tags, it’s very common to get duplicate results, because of the way relational databases work. Often you’ll want to make use of the distinct() method on QuerySets:

>>> Food.objects.filter(tags__name__in=["delicious", "red"])
[<Food: apple>, <Food: apple>]
>>> Food.objects.filter(tags__name__in=["delicious", "red"]).distinct()
[<Food: apple>]
@seros1521 commented on GitHub (Feb 28, 2022): From: https://django-taggit.readthedocs.io/en/latest/api.html#filtering: If you’re filtering on multiple tags, it’s very common to get duplicate results, because of the way relational databases work. Often you’ll want to make use of the distinct() method on QuerySets: ``` >>> Food.objects.filter(tags__name__in=["delicious", "red"]) [<Food: apple>, <Food: apple>] >>> Food.objects.filter(tags__name__in=["delicious", "red"]).distinct() [<Food: apple>] ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#6131