Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
seros1521 opened this issue Feb 22, 2022 · 4 comments · Fixed by #8793
Assignees
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application

Comments

@seros1521
Copy link
Contributor

seros1521 commented Feb 22, 2022

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!
@seros1521 seros1521 added the type: bug A confirmed report of unexpected behavior in the application label Feb 22, 2022
@seros1521
Copy link
Contributor Author

See also #8351

@seros1521
Copy link
Contributor Author

if added to the call

qs = self.filter(attrs)

.distinct()
the error disappears, but I'm not sure if this is the right way to solve it and it won't break anything.

@jeremystretch jeremystretch added the status: needs owner This issue is tentatively accepted pending a volunteer committed to its implementation label Feb 22, 2022
@jeremystretch
Copy link
Member

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!

@seros1521
Copy link
Contributor Author

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>]

@jeremystretch jeremystretch added status: accepted This issue has been accepted for implementation and removed status: needs owner This issue is tentatively accepted pending a volunteer committed to its implementation labels Mar 4, 2022
jeremystretch added a commit that referenced this issue Mar 7, 2022
Fixes #8715: eliminates duplicates when used in many-to-many field constraints
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants