Error with custom field using cable as the object #7571

Closed
opened 2025-12-29 20:25:34 +01:00 by adam · 1 comment
Owner

Originally created by @nahun on GitHub (Jan 27, 2023).

Originally assigned to: @arthanson on GitHub.

NetBox version

v3.4.2

Python version

3.9

Steps to Reproduce

  1. Create custom field with Content Types: Circuit, Type: Object, and Object type: DCIM > Cable
  2. Using the API, update a circuit object with the custom field data
import requests
payload = [{
    "id": 28,
    "custom_fields": {
        "cable": {"id": 59}
    }
}]
headers = {"Authorization": "Token <TOKEN>", "Content-Type": "application/json", "Accept": "application/json"}
r = requests.patch("https://demo.netbox.dev/api/circuits/circuits/", json=payload, headers=headers)
print(r.status_code)

Expected Behavior

Circuit custom field should be updated with reference to cable object

Observed Behavior

HTTP Response is a 500. The full traceback from my host running v3.4.2

level=ERROR,logger=django.request,msg=Internal Server Error: /api/circuits/circuits/
Traceback (most recent call last):
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 118, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 66, in bulk_partial_update
    return self.bulk_update(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 46, in bulk_update
    data = self.perform_bulk_update(qs, update_data, partial=partial)
  File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 58, in perform_bulk_update
    serializer.is_valid(raise_exception=True)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 227, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 426, in run_validation
    value = self.to_internal_value(data)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 483, in to_internal_value
    validated_value = field.run_validation(primitive_value)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/fields.py", line 547, in run_validation
    value = self.to_internal_value(data)
  File "/opt/netbox/netbox/extras/api/customfields.py", line 86, in to_internal_value
    data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id']
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 555, in data
    ret = super().data
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 255, in data
    self._data = self.to_representation(self.validated_data)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 522, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/relations.py", line 401, in to_representation
    url = self.get_url(value, self.view_name, request, format)
  File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/relations.py", line 337, in get_url
    lookup_value = getattr(obj, self.lookup_field)
AttributeError: 'collections.OrderedDict' object has no attribute 'pk'
Originally created by @nahun on GitHub (Jan 27, 2023). Originally assigned to: @arthanson on GitHub. ### NetBox version v3.4.2 ### Python version 3.9 ### Steps to Reproduce 1. Create custom field with Content Types: Circuit, Type: Object, and Object type: DCIM > Cable 2. Using the API, update a circuit object with the custom field data ```python import requests payload = [{ "id": 28, "custom_fields": { "cable": {"id": 59} } }] headers = {"Authorization": "Token <TOKEN>", "Content-Type": "application/json", "Accept": "application/json"} r = requests.patch("https://demo.netbox.dev/api/circuits/circuits/", json=payload, headers=headers) print(r.status_code) ``` ### Expected Behavior Circuit custom field should be updated with reference to cable object ### Observed Behavior HTTP Response is a 500. The full traceback from my host running v3.4.2 ``` level=ERROR,logger=django.request,msg=Internal Server Error: /api/circuits/circuits/ Traceback (most recent call last): File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view return view_func(*args, **kwargs) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view return self.dispatch(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 118, in dispatch return super().dispatch(request, *args, **kwargs) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 66, in bulk_partial_update return self.bulk_update(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 46, in bulk_update data = self.perform_bulk_update(qs, update_data, partial=partial) File "/opt/netbox/netbox/netbox/api/viewsets/mixins.py", line 58, in perform_bulk_update serializer.is_valid(raise_exception=True) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 227, in is_valid self._validated_data = self.run_validation(self.initial_data) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 426, in run_validation value = self.to_internal_value(data) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 483, in to_internal_value validated_value = field.run_validation(primitive_value) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/fields.py", line 547, in run_validation value = self.to_internal_value(data) File "/opt/netbox/netbox/extras/api/customfields.py", line 86, in to_internal_value data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id'] File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 555, in data ret = super().data File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 255, in data self._data = self.to_representation(self.validated_data) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 522, in to_representation ret[field.field_name] = field.to_representation(attribute) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/relations.py", line 401, in to_representation url = self.get_url(value, self.view_name, request, format) File "/opt/nbx_releases/v3.4.2/venv/lib/python3.9/site-packages/rest_framework/relations.py", line 337, in get_url lookup_value = getattr(obj, self.lookup_field) AttributeError: 'collections.OrderedDict' object has no attribute 'pk' ```
adam added the type: bugstatus: accepted labels 2025-12-29 20:25:34 +01:00
adam closed this issue 2025-12-29 20:25:34 +01:00
Author
Owner

@kkthxbye-code commented on GitHub (Feb 2, 2023):

I was able to replicate this, but I'm not sure I understand the correct fix.

@jeremystretch or @arthanson - can you take a look at this?

Issue is here:

a137cd6cbe/netbox/extras/api/customfields.py (L86)

serializer.data is not usable on an unsaved serializer. Calling .save() on the serializer fixes it, but obviously create a changelog for the cable. The docs seems to suggest that validated_data should be used instead of data, but it is empty. The call to is_valid() also passes when providing an invalid id for the cable.

@kkthxbye-code commented on GitHub (Feb 2, 2023): I was able to replicate this, but I'm not sure I understand the correct fix. @jeremystretch or @arthanson - can you take a look at this? Issue is here: https://github.com/netbox-community/netbox/blob/a137cd6cbefa4654cc8f69c367b3258d3ccdadaa/netbox/extras/api/customfields.py#L86 serializer.data is not usable on an unsaved serializer. Calling .save() on the serializer fixes it, but obviously create a changelog for the cable. The docs seems to suggest that `validated_data` should be used instead of `data`, but it is empty. The call to `is_valid()` also passes when providing an invalid id for the cable.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#7571