mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #2495: Enable deep-merging of config context data
This commit is contained in:
@ -3,6 +3,7 @@ v2.4.9 (FUTURE)
|
|||||||
## Enhancements
|
## Enhancements
|
||||||
|
|
||||||
* [#2089](https://github.com/digitalocean/netbox/issues/2089) - Add SONET interface form factors
|
* [#2089](https://github.com/digitalocean/netbox/issues/2089) - Add SONET interface form factors
|
||||||
|
* [#2495](https://github.com/digitalocean/netbox/issues/2495) - Enable deep-merging of config context data
|
||||||
* [#2597](https://github.com/digitalocean/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor
|
* [#2597](https://github.com/digitalocean/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor
|
||||||
|
|
||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
@ -18,7 +18,7 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
||||||
from utilities.utils import foreground_color
|
from utilities.utils import deepmerge, foreground_color
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .querysets import ConfigContextQuerySet
|
from .querysets import ConfigContextQuerySet
|
||||||
|
|
||||||
@ -727,11 +727,11 @@ class ConfigContextModel(models.Model):
|
|||||||
# Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs
|
# Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
for context in ConfigContext.objects.get_for_object(self):
|
for context in ConfigContext.objects.get_for_object(self):
|
||||||
data.update(context.data)
|
data = deepmerge(data, context.data)
|
||||||
|
|
||||||
# If the object has local config context data defined, that data overwrites all rendered data
|
# If the object has local config context data defined, merge it last
|
||||||
if self.local_context_data is not None:
|
if self.local_context_data is not None:
|
||||||
data.update(self.local_context_data)
|
data = deepmerge(data, self.local_context_data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
89
netbox/utilities/tests/test_utils.py
Normal file
89
netbox/utilities/tests/test_utils.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from utilities.utils import deepmerge
|
||||||
|
|
||||||
|
|
||||||
|
class DeepMergeTest(TestCase):
|
||||||
|
"""
|
||||||
|
Validate the behavior of the deepmerge() utility.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def test_deepmerge(self):
|
||||||
|
|
||||||
|
dict1 = {
|
||||||
|
'active': True,
|
||||||
|
'foo': 123,
|
||||||
|
'fruits': {
|
||||||
|
'orange': 1,
|
||||||
|
'apple': 2,
|
||||||
|
'pear': 3,
|
||||||
|
},
|
||||||
|
'vegetables': None,
|
||||||
|
'dairy': {
|
||||||
|
'milk': 1,
|
||||||
|
'cheese': 2,
|
||||||
|
},
|
||||||
|
'deepnesting': {
|
||||||
|
'foo': {
|
||||||
|
'a': 10,
|
||||||
|
'b': 20,
|
||||||
|
'c': 30,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dict2 = {
|
||||||
|
'active': False,
|
||||||
|
'bar': 456,
|
||||||
|
'fruits': {
|
||||||
|
'banana': 4,
|
||||||
|
'grape': 5,
|
||||||
|
},
|
||||||
|
'vegetables': {
|
||||||
|
'celery': 1,
|
||||||
|
'carrots': 2,
|
||||||
|
'corn': 3,
|
||||||
|
},
|
||||||
|
'dairy': None,
|
||||||
|
'deepnesting': {
|
||||||
|
'foo': {
|
||||||
|
'a': 100,
|
||||||
|
'd': 40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
merged = {
|
||||||
|
'active': False,
|
||||||
|
'foo': 123,
|
||||||
|
'bar': 456,
|
||||||
|
'fruits': {
|
||||||
|
'orange': 1,
|
||||||
|
'apple': 2,
|
||||||
|
'pear': 3,
|
||||||
|
'banana': 4,
|
||||||
|
'grape': 5,
|
||||||
|
},
|
||||||
|
'vegetables': {
|
||||||
|
'celery': 1,
|
||||||
|
'carrots': 2,
|
||||||
|
'corn': 3,
|
||||||
|
},
|
||||||
|
'dairy': None,
|
||||||
|
'deepnesting': {
|
||||||
|
'foo': {
|
||||||
|
'a': 100,
|
||||||
|
'b': 20,
|
||||||
|
'c': 30,
|
||||||
|
'd': 40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
deepmerge(dict1, dict2),
|
||||||
|
merged
|
||||||
|
)
|
@ -1,5 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import six
|
import six
|
||||||
@ -109,3 +110,16 @@ def serialize_object(obj, extra=None):
|
|||||||
data.update(extra)
|
data.update(extra)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def deepmerge(original, new):
|
||||||
|
"""
|
||||||
|
Deep merge two dictionaries (new into original) and return a new dict
|
||||||
|
"""
|
||||||
|
merged = OrderedDict(original)
|
||||||
|
for key, val in new.items():
|
||||||
|
if key in original and isinstance(original[key], dict) and isinstance(val, dict):
|
||||||
|
merged[key] = deepmerge(original[key], val)
|
||||||
|
else:
|
||||||
|
merged[key] = val
|
||||||
|
return merged
|
||||||
|
Reference in New Issue
Block a user