From 1bdfcd1dbedfc75706d009699901b693546da0e0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 3 Aug 2018 10:45:53 -0400 Subject: [PATCH] Fixes #2301: Fix model validation on assignment of ManyToMany fields via API patch --- netbox/extras/tests/test_api.py | 5 +++++ netbox/utilities/api.py | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 50d62c463..32551aff0 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -438,9 +438,13 @@ class ConfigContextTest(HttpStatusMixin, APITestCase): def test_update_configcontext(self): + region1 = Region.objects.create(name='Test Region 1', slug='test-region-1') + region2 = Region.objects.create(name='Test Region 2', slug='test-region-2') + data = { 'name': 'Test Config Context X', 'weight': 999, + 'regions': [region1.pk, region2.pk], 'data': {'foo': 'XXX'} } @@ -452,6 +456,7 @@ class ConfigContextTest(HttpStatusMixin, APITestCase): configcontext1 = ConfigContext.objects.get(pk=response.data['id']) self.assertEqual(configcontext1.name, data['name']) self.assertEqual(configcontext1.weight, data['weight']) + self.assertEqual(sorted([r.pk for r in configcontext1.regions.all()]), sorted(data['regions'])) self.assertEqual(configcontext1.data, data['data']) def test_delete_configcontext(self): diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index 4dc56cc8a..0ce207d6e 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -126,6 +126,8 @@ class SerializedPKRelatedField(PrimaryKeyRelatedField): # Serializers # +# TODO: We should probably take a fresh look at exactly what we're doing with this. There might be a more elegant +# way to enforce model validation on the serializer. class ValidatedModelSerializer(ModelSerializer): """ Extends the built-in ModelSerializer to enforce calling clean() on the associated model during validation. @@ -137,13 +139,13 @@ class ValidatedModelSerializer(ModelSerializer): attrs.pop('custom_fields', None) attrs.pop('tags', None) + # Skip ManyToManyFields + for field in self.Meta.model._meta.get_fields(): + if isinstance(field, ManyToManyField): + attrs.pop(field.name, None) + # Run clean() on an instance of the model if self.instance is None: - model = self.Meta.model - # Ignore ManyToManyFields for new instances (a PK is needed for validation) - for field in model._meta.get_fields(): - if isinstance(field, ManyToManyField) and field.name in attrs: - attrs.pop(field.name) instance = self.Meta.model(**attrs) else: instance = self.instance