mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Fixes #10241: Support referencing custom field related objects by attribute in addition to PK
This commit is contained in:
parent
90f15b8d55
commit
d5538c1ca3
@ -12,6 +12,7 @@
|
|||||||
* [#9223](https://github.com/netbox-community/netbox/issues/9223) - Fix serialization of array field values in change log
|
* [#9223](https://github.com/netbox-community/netbox/issues/9223) - Fix serialization of array field values in change log
|
||||||
* [#9878](https://github.com/netbox-community/netbox/issues/9878) - Fix spurious error message when rendering REST API docs
|
* [#9878](https://github.com/netbox-community/netbox/issues/9878) - Fix spurious error message when rendering REST API docs
|
||||||
* [#10236](https://github.com/netbox-community/netbox/issues/10236) - Fix TypeError exception when viewing PDU configured for three-phase power
|
* [#10236](https://github.com/netbox-community/netbox/issues/10236) - Fix TypeError exception when viewing PDU configured for three-phase power
|
||||||
|
* [#10241](https://github.com/netbox-community/netbox/issues/10241) - Support referencing custom field related objects by attribute in addition to PK
|
||||||
* [#10579](https://github.com/netbox-community/netbox/issues/10579) - Mark cable traces terminating to a provider network as complete
|
* [#10579](https://github.com/netbox-community/netbox/issues/10579) - Mark cable traces terminating to a provider network as complete
|
||||||
* [#10721](https://github.com/netbox-community/netbox/issues/10721) - Disable ordering by custom object field columns
|
* [#10721](https://github.com/netbox-community/netbox/issues/10721) - Disable ordering by custom object field columns
|
||||||
* [#10938](https://github.com/netbox-community/netbox/issues/10938) - `render_field` template tag should respect `label` kwarg
|
* [#10938](https://github.com/netbox-community/netbox/issues/10938) - `render_field` template tag should respect `label` kwarg
|
||||||
|
@ -5,6 +5,7 @@ from rest_framework.serializers import ValidationError
|
|||||||
from extras.choices import CustomFieldTypeChoices
|
from extras.choices import CustomFieldTypeChoices
|
||||||
from extras.models import CustomField
|
from extras.models import CustomField
|
||||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
|
from utilities.api import get_serializer_for_model
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -69,6 +70,23 @@ class CustomFieldsDataField(Field):
|
|||||||
"values."
|
"values."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Serialize object and multi-object values
|
||||||
|
for cf in self._get_custom_fields():
|
||||||
|
if cf.name in data and cf.type in (
|
||||||
|
CustomFieldTypeChoices.TYPE_OBJECT,
|
||||||
|
CustomFieldTypeChoices.TYPE_MULTIOBJECT
|
||||||
|
):
|
||||||
|
serializer_class = get_serializer_for_model(
|
||||||
|
model=cf.object_type.model_class(),
|
||||||
|
prefix=NESTED_SERIALIZER_PREFIX
|
||||||
|
)
|
||||||
|
many = cf.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT
|
||||||
|
serializer = serializer_class(data=data[cf.name], many=many, context=self.parent.context)
|
||||||
|
if serializer.is_valid():
|
||||||
|
data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id']
|
||||||
|
else:
|
||||||
|
raise ValidationError(f"Unknown related object(s): {data[cf.name]}")
|
||||||
|
|
||||||
# If updating an existing instance, start with existing custom_field_data
|
# If updating an existing instance, start with existing custom_field_data
|
||||||
if self.parent.instance:
|
if self.parent.instance:
|
||||||
data = {**self.parent.instance.custom_field_data, **data}
|
data = {**self.parent.instance.custom_field_data, **data}
|
||||||
|
@ -803,6 +803,57 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site2.custom_field_data['object_field'], original_cfvs['object_field'])
|
self.assertEqual(site2.custom_field_data['object_field'], original_cfvs['object_field'])
|
||||||
self.assertEqual(site2.custom_field_data['multiobject_field'], original_cfvs['multiobject_field'])
|
self.assertEqual(site2.custom_field_data['multiobject_field'], original_cfvs['multiobject_field'])
|
||||||
|
|
||||||
|
def test_specify_related_object_by_attr(self):
|
||||||
|
site1 = Site.objects.get(name='Site 1')
|
||||||
|
vlans = VLAN.objects.all()[:3]
|
||||||
|
url = reverse('dcim-api:site-detail', kwargs={'pk': site1.pk})
|
||||||
|
self.add_permissions('dcim.change_site')
|
||||||
|
|
||||||
|
# Set related objects by PK
|
||||||
|
data = {
|
||||||
|
'custom_fields': {
|
||||||
|
'object_field': vlans[0].pk,
|
||||||
|
'multiobject_field': [vlans[1].pk, vlans[2].pk],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(
|
||||||
|
response.data['custom_fields']['object_field']['id'],
|
||||||
|
vlans[0].pk
|
||||||
|
)
|
||||||
|
self.assertListEqual(
|
||||||
|
[obj['id'] for obj in response.data['custom_fields']['multiobject_field']],
|
||||||
|
[vlans[1].pk, vlans[2].pk]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set related objects by name
|
||||||
|
data = {
|
||||||
|
'custom_fields': {
|
||||||
|
'object_field': {
|
||||||
|
'name': vlans[0].name,
|
||||||
|
},
|
||||||
|
'multiobject_field': [
|
||||||
|
{
|
||||||
|
'name': vlans[1].name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': vlans[2].name
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(
|
||||||
|
response.data['custom_fields']['object_field']['id'],
|
||||||
|
vlans[0].pk
|
||||||
|
)
|
||||||
|
self.assertListEqual(
|
||||||
|
[obj['id'] for obj in response.data['custom_fields']['multiobject_field']],
|
||||||
|
[vlans[1].pk, vlans[2].pk]
|
||||||
|
)
|
||||||
|
|
||||||
def test_minimum_maximum_values_validation(self):
|
def test_minimum_maximum_values_validation(self):
|
||||||
site2 = Site.objects.get(name='Site 2')
|
site2 = Site.objects.get(name='Site 2')
|
||||||
url = reverse('dcim-api:site-detail', kwargs={'pk': site2.pk})
|
url = reverse('dcim-api:site-detail', kwargs={'pk': site2.pk})
|
||||||
|
Loading…
Reference in New Issue
Block a user