Plugin serializers won't be loaded without API #9069

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

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

Deployment Type

Self-hosted

NetBox Version

v3.7.0

Python Version

3.11

Steps to Reproduce

  1. Create a plugin skeleton as described in the plugin tutorial. Stop after Step 6, when all visual templates are available.
  2. Navigate to the main list of object (e.g. ACL list)
  3. Create a new object and save
  4. You'll see an error about missing serializers.
  5. Add a minimal serializers file as described in chapter 9. This is my minimal example:
    from netbox.api.serializers import NetBoxModelSerializer
    from ..models import IpList
    
    class IpListSerializer(NetBoxModelSerializer):
        class Meta:
            model = IpList
            fields = ('id', 'name', 'description')
    
  6. Try to add the object again. This step fails again.

Expected Behavior

As serializers have been added in api/serializers.py, these should be loaded and used. Creating an object should be possible now.

Observed Behavior

NetBox gives an error 500 and doesn't create the new object in the database. It gives the following traceback:

Internal Server Error: /plugins/blocklists/iplist/add/
Traceback (most recent call last):
  File "/opt/netbox/netbox/utilities/api.py", line 32, in get_serializer_for_model
    return dynamic_import(serializer_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/utilities/utils.py", line 127, in dynamic_import
    mod = getattr(mod, comp)
          ^^^^^^^^^^^^^^^^^^
AttributeError: module 'netbox_blocklists.api' has no attribute 'serializers'

During handling of the above exception, another exception occurred:

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 99, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.11/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 266, in post
    obj = form.save()
          ^^^^^^^^^^^
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/forms/models.py", line 542, in save
    self.instance.save()
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
    self.save_base(
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/base.py", line 892, in save_base
    post_save.send(
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 176, in send
    return [
           ^
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/extras/signals.py", line 94, in handle_changed_object
    enqueue_object(queue, instance, request.user, request.id, action)
  File "/opt/netbox/netbox/extras/events.py", line 67, in enqueue_object
    'data': serialize_for_event(instance),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/extras/events.py", line 28, in serialize_for_event
    serializer_class = get_serializer_for_model(instance.__class__)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/utilities/api.py", line 34, in get_serializer_for_model
    raise SerializerNotFound(
netbox.api.exceptions.SerializerNotFound: Could not determine serializer for netbox_blocklists.IpList with prefix ''

After some debugging it seems, NetBox refuses to load the api/serializers.py file on its own. Utilizing NetBox Shell, I can verify this file is not loadable standalone.

> ./deploy.sh test exec netbox /opt/netbox/netbox/manage.py nbshell
🧬 loaded config '/etc/netbox/config/configuration.py'
🧬 loaded config '/etc/netbox/config/auth_keycloak.py'
🧬 loaded config '/etc/netbox/config/extra.py'
🧬 loaded config '/etc/netbox/config/logging.py'
🧬 loaded config '/etc/netbox/config/plugins.py'
### NetBox interactive shell (7f6415413c64)
### Python 3.11.4 | Django 4.2.8 | NetBox 3.7.0
### lsmodels() will show available models. Use help(<model>) for more info.
>>> import netbox_blocklists            
>>> netbox_blocklists.api
netbox_blocklists.api 
>>> netbox_blocklists.api.serializers
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: module 'netbox_blocklists.api' has no attribute 'serializers'

Workaround

Adding an import statement in the previously empty __init__.py file resolves the issue for me:

from .serializers import *

Assumption

I think this might be some kind of dependency issue. Usually, when API endpoints will be deployed, api/urls.py is loaded first, which might load api/views.py and finally api/serializers.py, where in this order no errors are known from other plugin developers. However, when these are missing, maybe some dependencys for importing are not met?

Originally created by @alehaa on GitHub (Jan 8, 2024). ### Deployment Type Self-hosted ### NetBox Version v3.7.0 ### Python Version 3.11 ### Steps to Reproduce 1. Create a plugin skeleton as described in the [plugin tutorial](https://github.com/netbox-community/netbox-plugin-tutorial/tree/main). Stop after Step 6, when all visual templates are available. 2. Navigate to the main list of object (e.g. ACL list) 3. Create a new object and save 4. You'll see an error about missing serializers. 5. Add a minimal serializers file as described in [chapter 9](https://github.com/netbox-community/netbox-plugin-tutorial/blob/main/tutorial/step09-rest-api.md#create-model-serializers). This is my minimal example: ```python from netbox.api.serializers import NetBoxModelSerializer from ..models import IpList class IpListSerializer(NetBoxModelSerializer): class Meta: model = IpList fields = ('id', 'name', 'description') ``` 7. Try to add the object again. This step fails again. ### Expected Behavior As serializers have been added in `api/serializers.py`, these should be loaded and used. Creating an object should be possible now. ### Observed Behavior NetBox gives an error 500 and doesn't create the new object in the database. It gives the following traceback: ``` Internal Server Error: /plugins/blocklists/iplist/add/ Traceback (most recent call last): File "/opt/netbox/netbox/utilities/api.py", line 32, in get_serializer_for_model return dynamic_import(serializer_name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/utilities/utils.py", line 127, in dynamic_import mod = getattr(mod, comp) ^^^^^^^^^^^^^^^^^^ AttributeError: module 'netbox_blocklists.api' has no attribute 'serializers' During handling of the above exception, another exception occurred: 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 99, in dispatch return super().dispatch(request, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.11/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 266, in post obj = form.save() ^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.11/site-packages/django/forms/models.py", line 542, in save self.instance.save() File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save self.save_base( File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/base.py", line 892, in save_base post_save.send( File "/opt/netbox/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 176, in send return [ ^ File "/opt/netbox/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp> (receiver, receiver(signal=self, sender=sender, **named)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/signals.py", line 94, in handle_changed_object enqueue_object(queue, instance, request.user, request.id, action) File "/opt/netbox/netbox/extras/events.py", line 67, in enqueue_object 'data': serialize_for_event(instance), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/events.py", line 28, in serialize_for_event serializer_class = get_serializer_for_model(instance.__class__) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/utilities/api.py", line 34, in get_serializer_for_model raise SerializerNotFound( netbox.api.exceptions.SerializerNotFound: Could not determine serializer for netbox_blocklists.IpList with prefix '' ``` After some debugging it seems, NetBox refuses to load the `api/serializers.py` file on its own. Utilizing NetBox Shell, I can verify this file is not loadable standalone. ```Python > ./deploy.sh test exec netbox /opt/netbox/netbox/manage.py nbshell 🧬 loaded config '/etc/netbox/config/configuration.py' 🧬 loaded config '/etc/netbox/config/auth_keycloak.py' 🧬 loaded config '/etc/netbox/config/extra.py' 🧬 loaded config '/etc/netbox/config/logging.py' 🧬 loaded config '/etc/netbox/config/plugins.py' ### NetBox interactive shell (7f6415413c64) ### Python 3.11.4 | Django 4.2.8 | NetBox 3.7.0 ### lsmodels() will show available models. Use help(<model>) for more info. >>> import netbox_blocklists >>> netbox_blocklists.api netbox_blocklists.api >>> netbox_blocklists.api.serializers Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: module 'netbox_blocklists.api' has no attribute 'serializers' ``` **Workaround** Adding an `import` statement in the previously empty `__init__.py` file resolves the issue for me: ```python from .serializers import * ``` **Assumption** I think this might be some kind of dependency issue. Usually, when API endpoints will be deployed, `api/urls.py` is loaded first, which might load `api/views.py` and finally `api/serializers.py`, where in this order no errors are known from other plugin developers. However, when these are missing, maybe some dependencys for importing are not met?
adam closed this issue 2025-12-29 20:45:02 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#9069