mirror of
https://github.com/netbox-community/netbox.git
synced 2025-09-06 06:13:36 -06:00
Closes #20277: Add support for attribute assignment to deserialize_object()
This commit is contained in:
parent
47e4947ca0
commit
41f69b8f31
@ -51,30 +51,45 @@ def serialize_object(obj, resolve_tags=True, extra=None, exclude=None):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def deserialize_object(model, fields, pk=None):
|
def deserialize_object(model, data, pk=None):
|
||||||
"""
|
"""
|
||||||
Instantiate an object from the given model and field data. Functions as
|
Instantiate an object from the given model and field data. Functions as
|
||||||
the complement to serialize_object().
|
the complement to serialize_object().
|
||||||
"""
|
"""
|
||||||
content_type = ContentType.objects.get_for_model(model)
|
content_type = ContentType.objects.get_for_model(model)
|
||||||
|
data = data.copy()
|
||||||
m2m_data = {}
|
m2m_data = {}
|
||||||
|
|
||||||
# Account for custom field data
|
# Account for custom field data
|
||||||
if 'custom_fields' in fields:
|
if 'custom_fields' in data:
|
||||||
fields['custom_field_data'] = fields.pop('custom_fields')
|
data['custom_field_data'] = data.pop('custom_fields')
|
||||||
|
|
||||||
# Pop any assigned tags to handle the M2M relationships manually
|
# Pop any assigned tags to handle the M2M relationships manually
|
||||||
if is_taggable(model) and fields.get('tags'):
|
if is_taggable(model) and data.get('tags'):
|
||||||
Tag = apps.get_model('extras', 'Tag')
|
Tag = apps.get_model('extras', 'Tag')
|
||||||
m2m_data['tags'] = Tag.objects.filter(name__in=fields.pop('tags'))
|
m2m_data['tags'] = Tag.objects.filter(name__in=data.pop('tags'))
|
||||||
|
|
||||||
|
# Separate any non-field attributes for assignment after deserialization of the object
|
||||||
|
model_fields = [
|
||||||
|
field.name for field in model._meta.get_fields()
|
||||||
|
]
|
||||||
|
attrs = {
|
||||||
|
name: data.pop(name) for name in list(data.keys())
|
||||||
|
if name not in model_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
# Employ Django's native Python deserializer to produce the instance
|
||||||
data = {
|
data = {
|
||||||
'model': '.'.join(content_type.natural_key()),
|
'model': '.'.join(content_type.natural_key()),
|
||||||
'pk': pk,
|
'pk': pk,
|
||||||
'fields': fields,
|
'fields': data,
|
||||||
}
|
}
|
||||||
instance = list(serializers.deserialize('python', [data]))[0]
|
instance = list(serializers.deserialize('python', [data]))[0]
|
||||||
|
|
||||||
|
# Assign non-field attributes
|
||||||
|
for name, value in attrs.items():
|
||||||
|
setattr(instance.object, name, value)
|
||||||
|
|
||||||
# Apply any additional M2M assignments
|
# Apply any additional M2M assignments
|
||||||
instance.m2m_data.update(**m2m_data)
|
instance.m2m_data.update(**m2m_data)
|
||||||
|
|
||||||
|
49
netbox/utilities/tests/test_serialization.py
Normal file
49
netbox/utilities/tests/test_serialization.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from dcim.choices import SiteStatusChoices
|
||||||
|
from dcim.models import Site
|
||||||
|
from extras.models import Tag
|
||||||
|
from utilities.serialization import deserialize_object, serialize_object
|
||||||
|
|
||||||
|
|
||||||
|
class SerializationTestCase(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
tags = (
|
||||||
|
Tag(name='Tag 1', slug='tag-1'),
|
||||||
|
Tag(name='Tag 2', slug='tag-2'),
|
||||||
|
Tag(name='Tag 3', slug='tag-3'),
|
||||||
|
)
|
||||||
|
Tag.objects.bulk_create(tags)
|
||||||
|
|
||||||
|
def test_serialize_object(self):
|
||||||
|
site = Site.objects.create(
|
||||||
|
name='Site 1',
|
||||||
|
slug='site=1',
|
||||||
|
description='Ignore me',
|
||||||
|
)
|
||||||
|
site.tags.set(Tag.objects.all())
|
||||||
|
|
||||||
|
data = serialize_object(site, extra={'foo': 123}, exclude=['description'])
|
||||||
|
self.assertEqual(data['name'], site.name)
|
||||||
|
self.assertEqual(data['slug'], site.slug)
|
||||||
|
self.assertEqual(data['tags'], [tag.name for tag in Tag.objects.all()])
|
||||||
|
self.assertEqual(data['foo'], 123)
|
||||||
|
self.assertNotIn('description', data)
|
||||||
|
|
||||||
|
def test_deserialize_object(self):
|
||||||
|
data = {
|
||||||
|
'name': 'Site 1',
|
||||||
|
'slug': 'site-1',
|
||||||
|
'tags': ['Tag 1', 'Tag 2', 'Tag 3'],
|
||||||
|
'foo': 123,
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = deserialize_object(Site, data, pk=123)
|
||||||
|
self.assertEqual(instance.object.pk, 123)
|
||||||
|
self.assertEqual(instance.object.name, data['name'])
|
||||||
|
self.assertEqual(instance.object.slug, data['slug'])
|
||||||
|
self.assertEqual(instance.object.status, SiteStatusChoices.STATUS_ACTIVE) # Default field value
|
||||||
|
self.assertEqual(instance.object.foo, data['foo']) # Non-field attribute
|
||||||
|
self.assertEqual(list(instance.m2m_data['tags']), list(Tag.objects.all()))
|
Loading…
Reference in New Issue
Block a user