Plugin models always require RestrictedQuerySet #9098

Closed
opened 2025-12-29 20:45:25 +01:00 by adam · 4 comments
Owner

Originally created by @alehaa on GitHub (Jan 17, 2024).

Deployment Type

Self-hosted

NetBox Version

v3.7.0

Python Version

3.11

Steps to Reproduce

  1. Create minimal plugin following the official documentation.
  2. For models, create a minimal using the mixin classes:
    from django.db import models
    from netbox.models.features import ExportTemplatesMixin, TagsMixin
    
    class MyModel(ExportTemplatesMixin, TagsMixin, models.Model):
        foo = models.CharField()
    
  3. Use this model with generic views.
  4. Try adding a new model in the web UI

Expected Behavior

Form to create a new model will appear. Model should work as stated in the documentation.

Observed Behavior

NetBox crashes, as model's queryset doesn't have a property named restricted.

Environment:


Request Method: GET
Request URL: http://localhost:8000/plugins/blocklists/ip/lists/entries/add/

Django Version: 4.2.8
Python Version: 3.11.4
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'django.forms',
 'corsheaders',
 'debug_toolbar',
 'graphiql_debug_toolbar',
 'django_filters',
 'django_tables2',
 'django_prometheus',
 'graphene_django',
 'mptt',
 'rest_framework',
 'social_django',
 'taggit',
 'timezone_field',
 'core',
 'account',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'vpn',
 'wireless',
 'django_rq',
 'drf_spectacular',
 'drf_spectacular_sidecar',
 'netbox_blocklists.BlocklistsConfig']
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.RemoteUserMiddleware',
 'netbox.middleware.CoreMiddleware',
 'netbox.middleware.MaintenanceModeMiddleware',
 'django_prometheus.middleware.PrometheusAfterMiddleware']



Traceback (most recent call last):
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 172, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/views/generic/base.py", line 26, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/utilities/views.py", line 96, in dispatch
    if not self.has_permission():
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/utilities/views.py", line 82, in has_permission
    self.queryset = self.queryset.restrict(user, action)
                    ^^^^^^^^^^^^^^^^^^^^^^

Exception Type: AttributeError at /plugins/blocklists/ip/lists/entries/add/
Exception Value: 'QuerySet' object has no attribute 'restrict'

Workaround:

As in NetBoxModel, add a RestrictedQuerySet:

from utilities.querysets import RestrictedQuerySet

class MyModel(ExportTemplatesMixin, TagsMixin, models.Model):
    objects = RestrictedQuerySet.as_manager()

Possible solutions:

  1. This could be a permanent solution. Documentation should be changed accordingly.

  2. I identified the cause of the problem in has_permission method of ObjectPermissionRequiredMixin. One could add a guard like in other classes:

    if issubclass(self.queryset.__class__, RestrictedQuerySet):
    

    I could provide a patch for adding the if-clause.

Originally created by @alehaa on GitHub (Jan 17, 2024). ### Deployment Type Self-hosted ### NetBox Version v3.7.0 ### Python Version 3.11 ### Steps to Reproduce 1. Create minimal plugin following the [official documentation](https://docs.netbox.dev/en/stable/plugins/development/models/). 2. For models, create a minimal using the mixin classes: ```Python from django.db import models from netbox.models.features import ExportTemplatesMixin, TagsMixin class MyModel(ExportTemplatesMixin, TagsMixin, models.Model): foo = models.CharField() ``` 3. Use this model with generic views. 4. Try adding a new model in the web UI ### Expected Behavior Form to create a new model will appear. Model should work as stated in the documentation. ### Observed Behavior NetBox crashes, as model's queryset doesn't have a property named `restricted`. ``` Environment: Request Method: GET Request URL: http://localhost:8000/plugins/blocklists/ip/lists/entries/add/ Django Version: 4.2.8 Python Version: 3.11.4 Installed Applications: ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'django.forms', 'corsheaders', 'debug_toolbar', 'graphiql_debug_toolbar', 'django_filters', 'django_tables2', 'django_prometheus', 'graphene_django', 'mptt', 'rest_framework', 'social_django', 'taggit', 'timezone_field', 'core', 'account', 'circuits', 'dcim', 'ipam', 'extras', 'tenancy', 'users', 'utilities', 'virtualization', 'vpn', 'wireless', 'django_rq', 'drf_spectacular', 'drf_spectacular_sidecar', 'netbox_blocklists.BlocklistsConfig'] 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.RemoteUserMiddleware', 'netbox.middleware.CoreMiddleware', 'netbox.middleware.MaintenanceModeMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware'] Traceback (most recent call last): File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) ^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view return self.dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 172, in dispatch return super().dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/views/generic/base.py", line 26, in dispatch return super().dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/utilities/views.py", line 96, in dispatch if not self.has_permission(): ^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/utilities/views.py", line 82, in has_permission self.queryset = self.queryset.restrict(user, action) ^^^^^^^^^^^^^^^^^^^^^^ Exception Type: AttributeError at /plugins/blocklists/ip/lists/entries/add/ Exception Value: 'QuerySet' object has no attribute 'restrict' ``` ### Workaround: As in `NetBoxModel`, add a `RestrictedQuerySet`: ```Python from utilities.querysets import RestrictedQuerySet class MyModel(ExportTemplatesMixin, TagsMixin, models.Model): objects = RestrictedQuerySet.as_manager() ``` **Possible solutions:** 1. This could be a permanent solution. Documentation should be changed accordingly. 2. I identified the cause of the problem in [`has_permission` method of ObjectPermissionRequiredMixin](https://github.com/netbox-community/netbox/blob/530a15e90689f8dedb5b4006f79c35d4ba4a5ca4/netbox/utilities/views.py#L82C1-L82C1). One could add a guard like in other classes: ```Python if issubclass(self.queryset.__class__, RestrictedQuerySet): ``` I could provide a patch for adding the if-clause.
adam closed this issue 2025-12-29 20:45:26 +01:00
Author
Owner

@abhi1693 commented on GitHub (Jan 17, 2024):

This is not a bug. If you choose to use subset of features in views that requires to have all functionality available, you'll run into such issues. If you don't want to consume pre built functionalities, I'd suggest either override the needed methods or use django stock views

@abhi1693 commented on GitHub (Jan 17, 2024): This is not a bug. If you choose to use subset of features in views that requires to have all functionality available, you'll run into such issues. If you don't want to consume pre built functionalities, I'd suggest either override the needed methods or use django stock views
Author
Owner

@alehaa commented on GitHub (Jan 18, 2024):

I'm aware that my code only uses a subset of the functionality. However, my understanding of the documentation is that NetBoxModel is just a shortcut for using all the mixin classes, and if you don't want bookmarking to be available, for example, you just drop that mixin and use all the others. Maybe we can make this clearer in the plugin documentation? Also, would it be allowed to use RestrictedQuerySet in plugins, as I can't find any documentation about it, not even for general development.

@alehaa commented on GitHub (Jan 18, 2024): I'm aware that my code only uses a subset of the functionality. However, my understanding of the documentation is that `NetBoxModel` is just a shortcut for using all the mixin classes, and if you don't want bookmarking to be available, for example, you just drop that mixin and use all the others. Maybe we can make this clearer in the plugin documentation? Also, would it be allowed to use `RestrictedQuerySet` in plugins, as I can't find any documentation about it, not even for general development.
Author
Owner

@DanSheps commented on GitHub (Jan 18, 2024):

I can understand where the confusion might come in, however I think resources would be best put towards updating the documentation instead to clarify that using any of the "Object Views" requires the restricted queryset (which you can also use yourself)

@DanSheps commented on GitHub (Jan 18, 2024): I can understand where the confusion might come in, however I think resources would be best put towards updating the documentation instead to clarify that using any of the "Object Views" requires the restricted queryset (which you can also use yourself)
Author
Owner

@jeremystretch commented on GitHub (Jan 18, 2024):

Closing this out as it's not a bug. If you want to propose a specific change, please raise a feature request.

@jeremystretch commented on GitHub (Jan 18, 2024): Closing this out as it's not a bug. If you want to propose a **specific change**, please raise a [feature request](https://github.com/netbox-community/netbox/issues/new?assignees=&labels=type%3A+feature&projects=&template=feature_request.yaml).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#9098