Repository: digitalocean/pynetbox
Pull request #197 (2019-11-16 02:32:50)

Implements JsonField object
Is used on models to indicate that a dict should not be turns into a
Record object. Also implements `local_context_data` as such a field.


Patch:
diff --git a/pynetbox/core/response.py b/pynetbox/core/response.py
index 45b026c..56b84ca 100644
--- a/pynetbox/core/response.py
+++ b/pynetbox/core/response.py
@@ -16,11 +16,6 @@ limitations under the License.
 from pynetbox.core.query import Request
 from pynetbox.core.util import Hashabledict
 
-# List of fields that contain a dict but are not to be converted into
-# Record objects.
-JSON_FIELDS = (
-    "custom_fields", "data", "config_context", "object_data"
-)
 
 # List of fields that are lists but should be treated as sets.
 LIST_AS_SET = ("tags", "tagged_vlans")
@@ -61,6 +56,12 @@ def flatten_custom(custom_dict):
     }
 
 
+class JsonField(object):
+    """Explicit field type for values that are not to be converted
+    to a Record object"""
+    _json_field = True
+
+
 class Record(object):
     """Create python objects from netbox API responses.
 
@@ -235,25 +236,25 @@ class Record(object):
             return list_item
 
         for k, v in values.items():
+            if isinstance(v, dict):
+                lookup = getattr(self.__class__, k, None)
+                if k == "custom_fields" or hasattr(lookup, "_json_field"):
+                    self._add_cache((k, v.copy()))
+                    setattr(self, k, v)
+                    continue
+                if lookup:
+                    v = lookup(v, self.api, self.endpoint)
+                else:
+                    v = self.default_ret(v, self.api, self.endpoint)
+                self._add_cache((k, v))
 
-            if k not in JSON_FIELDS:
-                if isinstance(v, dict):
-                    lookup = getattr(self.__class__, k, None)
-                    if lookup:
-                        v = lookup(v, self.api, self.endpoint)
-                    else:
-                        v = self.default_ret(v, self.api, self.endpoint)
-                    self._add_cache((k, v))
-
-                elif isinstance(v, list):
-                    v = [list_parser(i) for i in v]
-                    to_cache = list(v)
-                    self._add_cache((k, to_cache))
+            elif isinstance(v, list):
+                v = [list_parser(i) for i in v]
+                to_cache = list(v)
+                self._add_cache((k, to_cache))
 
-                else:
-                    self._add_cache((k, v))
             else:
-                self._add_cache((k, v.copy()))
+                self._add_cache((k, v))
             setattr(self, k, v)
 
     def _compare(self):
diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py
index e151ce5..9ceade2 100644
--- a/pynetbox/models/dcim.py
+++ b/pynetbox/models/dcim.py
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 """
-from pynetbox.core.response import Record
+from pynetbox.core.response import Record, JsonField
 from pynetbox.core.endpoint import RODetailEndpoint
 from pynetbox.models.ipam import IpAddresses
 from pynetbox.models.circuits import Circuits
@@ -44,6 +44,8 @@ class Devices(Record):
     primary_ip = IpAddresses
     primary_ip4 = IpAddresses
     primary_ip6 = IpAddresses
+    local_context_data = JsonField
+    config_context = JsonField
 
     @property
     def napalm(self):
diff --git a/pynetbox/models/extras.py b/pynetbox/models/extras.py
index 6bf7f07..0848a70 100644
--- a/pynetbox/models/extras.py
+++ b/pynetbox/models/extras.py
@@ -13,9 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 """
-from pynetbox.core.response import Record
+from pynetbox.core.response import Record, JsonField
+
+
+class ConfigContexts(Record):
+    data = JsonField
 
 
 class ObjectChanges(Record):
+    object_data = JsonField
+
     def __str__(self):
         return self.request_id


Test patch:
diff --git a/tests/fixtures/dcim/device.json b/tests/fixtures/dcim/device.json
index 4320349..b29fe44 100644
--- a/tests/fixtures/dcim/device.json
+++ b/tests/fixtures/dcim/device.json
@@ -65,8 +65,11 @@
     },
     "primary_ip6": null,
     "comments": "",
+    "local_context_data": {
+        "testing": "test"
+    },
     "custom_fields": {},
     "config_context": {
         "test_key": "test_val"
     }
-}
\ No newline at end of file
+}
diff --git a/tests/test_dcim.py b/tests/test_dcim.py
index c00e1d8..7aed989 100644
--- a/tests/test_dcim.py
+++ b/tests/test_dcim.py
@@ -170,6 +170,7 @@ class DeviceTestCase(Generic.Tests):
         self.assertTrue(isinstance(ret.primary_ip4, pynetbox.models.ipam.IpAddresses))
         self.assertTrue(isinstance(ret.config_context, dict))
         self.assertTrue(isinstance(ret.custom_fields, dict))
+        self.assertTrue(isinstance(ret.local_context_data, dict))
         mock.assert_called_with(
             'http://localhost:8000/api/{}/{}/1/'.format(
                 self.app,