NoReverseMatch exceptions for plugins without REST API #10345

Closed
opened 2025-12-29 21:30:20 +01:00 by adam · 5 comments
Owner

Originally created by @alehaa on GitHub (Oct 10, 2024).

Deployment Type

Self-hosted

Triage priority

I volunteer to perform this work (if approved)

NetBox Version

v4.1.3

Python Version

3.11

Steps to Reproduce

  1. Install a NetBox plugin.
  2. Remove API URLs by replacing api/urls.py by this code:
    from netbox.api.routers import NetBoxRouter
    
    router = NetBoxRouter()
    urlpatterns = router.urls
    
  3. Edit a model of the plugin via UI and save.

Expected Behavior

  • Model changes will be saved to the database.
  • REST API for this plugin is not available. According to the documentation this should be valid behavior, as it's just a "can".

Observed Behavior

An exception is raised.

Traceback (most recent call last):
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/relations.py", line 396, in to_representation
    url = self.get_url(value, self.view_name, request, format)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/api/serializers/fields.py", line 37, in get_url
    return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/reverse.py", line 47, in reverse
    url = _reverse(viewname, args, kwargs, request, format, **extra)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/reverse.py", line 60, in _reverse
    url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/urls/base.py", line 88, in reverse
    return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/urls/resolvers.py", line 851, in _reverse_with_prefix
    raise NoReverseMatch(msg)
    ^^^^^^^^^^^^^^^^^^^^^^^^^

During handling of the above exception (Reverse for 'domainlistentry-detail' not found. 'domainlistentry-detail' is not a valid view function or pattern name.), another exception occurred:
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/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.12/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 182, 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 125, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/utilities/views.py", line 39, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 278, in post
    obj = form.save()
          ^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/forms/models.py", line 552, in save
    self.instance.save()
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/base.py", line 822, in save
    self.save_base(
    ^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/base.py", line 924, in save_base
    post_save.send(
    ^
  File "/opt/netbox/venv/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/core/signals.py", line 102, in handle_changed_object
    enqueue_event(queue, instance, request.user, request.id, event_type)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/extras/events.py", line 77, in enqueue_event
    'data': serialize_for_event(instance),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/extras/events.py", line 35, in serialize_for_event
    return serializer.data
           ^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 571, in data
    ret = super().data
          ^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 249, in data
    self._data = self.to_representation(self.instance)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 538, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/relations.py", line 411, in to_representation
    raise ImproperlyConfigured(msg % self.view_name)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Exception Type: ImproperlyConfigured at /plugins/blocklists/domain/lists/entries/add/
Exception Value: Could not resolve URL for hyperlinked relationship using view name "". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
Originally created by @alehaa on GitHub (Oct 10, 2024). ### Deployment Type Self-hosted ### Triage priority I volunteer to perform this work (if approved) ### NetBox Version v4.1.3 ### Python Version 3.11 ### Steps to Reproduce 1. Install a NetBox plugin. 2. Remove API URLs by replacing `api/urls.py` by this code: ```Python from netbox.api.routers import NetBoxRouter router = NetBoxRouter() urlpatterns = router.urls ``` 3. Edit a model of the plugin via UI and save. ### Expected Behavior * Model changes will be saved to the database. * REST API for this plugin is not available. According to the documentation this should be valid behavior, as it's just a "can". ### Observed Behavior An exception is raised. ``` Traceback (most recent call last): File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/relations.py", line 396, in to_representation url = self.get_url(value, self.view_name, request, format) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/api/serializers/fields.py", line 37, in get_url return self.reverse(view_name, kwargs=kwargs, request=request, format=format) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/reverse.py", line 47, in reverse url = _reverse(viewname, args, kwargs, request, format, **extra) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/reverse.py", line 60, in _reverse url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/urls/base.py", line 88, in reverse return resolver._reverse_with_prefix(view, prefix, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/urls/resolvers.py", line 851, in _reverse_with_prefix raise NoReverseMatch(msg) ^^^^^^^^^^^^^^^^^^^^^^^^^ During handling of the above exception (Reverse for 'domainlistentry-detail' not found. 'domainlistentry-detail' is not a valid view function or pattern name.), another exception occurred: File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) ^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/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.12/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 182, 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 125, in dispatch return super().dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/utilities/views.py", line 39, in dispatch return super().dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 143, in dispatch return handler(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 278, in post obj = form.save() ^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/forms/models.py", line 552, in save self.instance.save() ^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/base.py", line 822, in save self.save_base( ^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/base.py", line 924, in save_base post_save.send( ^ File "/opt/netbox/venv/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send response = receiver(signal=self, sender=sender, **named) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/core/signals.py", line 102, in handle_changed_object enqueue_event(queue, instance, request.user, request.id, event_type) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/events.py", line 77, in enqueue_event 'data': serialize_for_event(instance), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/events.py", line 35, in serialize_for_event return serializer.data ^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 571, in data ret = super().data ^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 249, in data self._data = self.to_representation(self.instance) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 538, in to_representation ret[field.field_name] = field.to_representation(attribute) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/relations.py", line 411, in to_representation raise ImproperlyConfigured(msg % self.view_name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Exception Type: ImproperlyConfigured at /plugins/blocklists/domain/lists/entries/add/ Exception Value: Could not resolve URL for hyperlinked relationship using view name "". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field. ```
adam added the type: bugpending closurestatus: under reviewnetbox labels 2025-12-29 21:30:20 +01:00
adam closed this issue 2025-12-29 21:30:20 +01:00
Author
Owner

@alehaa commented on GitHub (Oct 10, 2024):

I believe I can track this down to #15156, where the serializers got automatic URL lookup. The new BaseNetBoxHyperlinkedIdentityField.get_url() method even states:

May raise a NoReverseMatch if the view_name and lookup_field attributes are not configured to correctly match the URL conf.

To solve this easily, I suggest to change the documentation. The serializers are indeed required for NetBox to work properly. However, if someone doesn't want to provide the REST API (or hasn't developed it yet), it is sufficient to simply add url = None to the serializer.

@alehaa commented on GitHub (Oct 10, 2024): I believe I can track this down to #15156, where the serializers got automatic URL lookup. The new `BaseNetBoxHyperlinkedIdentityField.get_url()` method even states: > May raise a `NoReverseMatch` if the `view_name` and `lookup_field` attributes are not configured to correctly match the URL conf. To solve this easily, I suggest to change the documentation. The serializers are indeed required for NetBox to work properly. However, if someone doesn't want to provide the REST API (or hasn't developed it yet), it is sufficient to simply add `url = None` to the serializer.
Author
Owner

@jeremystretch commented on GitHub (Oct 10, 2024):

The serializers are indeed required for NetBox to work properly

Maybe we could tweak serialize_for_event() to fall back to using a generic JSON serializer (i.e. serialize_object()) when a custom serializer does not exist for a model.

@jeremystretch commented on GitHub (Oct 10, 2024): > The serializers are indeed required for NetBox to work properly Maybe we could tweak `serialize_for_event()` to fall back to using a generic JSON serializer (i.e. `serialize_object()`) when a custom serializer does not exist for a model.
Author
Owner

@alehaa commented on GitHub (Oct 10, 2024):

This would be possible. However events may require a defined set of fields that are not provided by the default serialize_object() (e.g. display). Switching the serializer could make things worse if administrators have to worry about what type of payload is being passed.

Maybe we could catch the NoReverseMatch exception in BaseNetBoxHyperlinkedIdentityField.get_url() simply returning no or an empty url if no REST API endpoint is available for this model.

@alehaa commented on GitHub (Oct 10, 2024): This would be possible. However events may require a defined set of fields that are not provided by the default `serialize_object()` (e.g. `display`). Switching the serializer could make things worse if administrators have to worry about what type of payload is being passed. Maybe we could catch the `NoReverseMatch` exception in `BaseNetBoxHyperlinkedIdentityField.get_url()` simply returning no or an empty `url` if no REST API endpoint is available for this model.
Author
Owner

@github-actions[bot] commented on GitHub (Apr 24, 2025):

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. Do not attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our contributing guide.

@github-actions[bot] commented on GitHub (Apr 24, 2025): This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/main/CONTRIBUTING.md).
Author
Owner

@github-actions[bot] commented on GitHub (May 25, 2025):

This issue has been automatically closed due to lack of activity. In an effort to reduce noise, please do not comment any further. Note that the core maintainers may elect to reopen this issue at a later date if deemed necessary.

@github-actions[bot] commented on GitHub (May 25, 2025): This issue has been automatically closed due to lack of activity. In an effort to reduce noise, please do not comment any further. Note that the core maintainers may elect to reopen this issue at a later date if deemed necessary.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10345