From 41c92483a0b9b820dd0ca0c43c6c6d6e560043ed Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 5 Apr 2023 12:29:32 -0500 Subject: [PATCH] #12087 - Fix Bulk Edit update when M2M operations are present. (#12169) * #12087 - Fix Bulk Edit update when M2M operations are present. * #12087 - Minor tweaks * Change .set() to .clear() Co-authored-by: Jeremy Stretch * #12087 - Update comments --------- Co-authored-by: Jeremy Stretch --- netbox/netbox/views/generic/bulk_views.py | 41 +++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 6060475d880..c0efe3014be 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -500,6 +500,21 @@ def _update_objects(self, form, request): ] nullified_fields = request.POST.getlist('_nullify') updated_objects = [] + model_fields = {} + m2m_fields = {} + + # Build list of model fields and m2m fields for later iteration + for name in standard_fields: + try: + model_field = self.queryset.model._meta.get_field(name) + if isinstance(model_field, (ManyToManyField, ManyToManyRel)): + m2m_fields[name] = model_field + else: + model_fields[name] = model_field + + except FieldDoesNotExist: + # This form field is used to modify a field rather than set its value directly + model_fields[name] = None for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']): @@ -508,25 +523,10 @@ def _update_objects(self, form, request): obj.snapshot() # Update standard fields. If a field is listed in _nullify, delete its value. - for name in standard_fields: - - try: - model_field = self.queryset.model._meta.get_field(name) - except FieldDoesNotExist: - # This form field is used to modify a field rather than set its value directly - model_field = None - + for name, model_field in model_fields.items(): # Handle nullification if name in form.nullable_fields and name in nullified_fields: - if isinstance(model_field, ManyToManyField): - getattr(obj, name).set([]) - else: - setattr(obj, name, None if model_field.null else '') - - # ManyToManyFields - elif isinstance(model_field, (ManyToManyField, ManyToManyRel)): - if form.cleaned_data[name]: - getattr(obj, name).set(form.cleaned_data[name]) + setattr(obj, name, None if model_field.null else '') # Normal fields elif name in form.changed_data: setattr(obj, name, form.cleaned_data[name]) @@ -544,6 +544,13 @@ def _update_objects(self, form, request): obj.save() updated_objects.append(obj) + # Handle M2M fields after save + for name, m2m_field in m2m_fields.items(): + if name in form.nullable_fields and name in nullified_fields: + getattr(obj, name).clear() + else: + getattr(obj, name).set(form.cleaned_data[name]) + # Add/remove tags if form.cleaned_data.get('add_tags', None): obj.tags.add(*form.cleaned_data['add_tags'])