mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-22 09:30:09 +01:00
Merge branch 'develop' into feature
This commit is contained in:
@@ -5,6 +5,7 @@ from rest_framework.serializers import ValidationError
|
||||
from extras.choices import CustomFieldTypeChoices
|
||||
from extras.models import CustomField
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from utilities.api import get_serializer_for_model
|
||||
|
||||
|
||||
#
|
||||
@@ -69,6 +70,23 @@ class CustomFieldsDataField(Field):
|
||||
"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 self.parent.instance:
|
||||
data = {**self.parent.instance.custom_field_data, **data}
|
||||
|
||||
@@ -14,7 +14,6 @@ from .choices import ObjectChangeActionChoices
|
||||
from .models import ConfigRevision, CustomField, ObjectChange
|
||||
from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
|
||||
|
||||
|
||||
#
|
||||
# Change logging/webhooks
|
||||
#
|
||||
@@ -100,9 +99,6 @@ def handle_deleted_object(sender, instance, **kwargs):
|
||||
"""
|
||||
Fires when an object is deleted.
|
||||
"""
|
||||
if not hasattr(instance, 'to_objectchange'):
|
||||
return
|
||||
|
||||
# Get the current request, or bail if not set
|
||||
request = current_request.get()
|
||||
if request is None:
|
||||
@@ -110,6 +106,8 @@ def handle_deleted_object(sender, instance, **kwargs):
|
||||
|
||||
# Record an ObjectChange if applicable
|
||||
if hasattr(instance, 'to_objectchange'):
|
||||
if hasattr(instance, 'snapshot') and not getattr(instance, '_prechange_snapshot', None):
|
||||
instance.snapshot()
|
||||
objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
|
||||
objectchange.user = request.user
|
||||
objectchange.request_id = request.id
|
||||
|
||||
@@ -854,6 +854,57 @@ class CustomFieldAPITest(APITestCase):
|
||||
list(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):
|
||||
site2 = Site.objects.get(name='Site 2')
|
||||
url = reverse('dcim-api:site-detail', kwargs={'pk': site2.pk})
|
||||
|
||||
Reference in New Issue
Block a user