diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 79f980028..6780cfba7 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -71,10 +71,10 @@ def get_custom_fields_for_model(content_type, filterable_only=False, bulk_edit=F class CustomFieldForm(forms.ModelForm): - custom_fields = [] def __init__(self, *args, **kwargs): + self.custom_fields = [] self.obj_type = ContentType.objects.get_for_model(self._meta.model) super(CustomFieldForm, self).__init__(*args, **kwargs) @@ -125,21 +125,24 @@ class CustomFieldForm(forms.ModelForm): class CustomFieldBulkEditForm(BulkEditForm): - custom_fields = [] - - def __init__(self, model, *args, **kwargs): - - self.obj_type = ContentType.objects.get_for_model(model) + def __init__(self, *args, **kwargs): super(CustomFieldBulkEditForm, self).__init__(*args, **kwargs) + self.custom_fields = [] + self.obj_type = ContentType.objects.get_for_model(self.model) + # Add all applicable CustomFields to the form - custom_fields = [] - for name, field in get_custom_fields_for_model(self.obj_type, bulk_edit=True).items(): + custom_fields = get_custom_fields_for_model(self.obj_type, bulk_edit=True).items() + for name, field in custom_fields: + # Annotate non-required custom fields as nullable + if not field.required: + self.nullable_fields.append(name) field.required = False self.fields[name] = field - custom_fields.append(name) - self.custom_fields = custom_fields + # Annotate this as a custom field + self.custom_fields.append(name) + print(self.nullable_fields) class CustomFieldFilterForm(forms.Form): diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index e27b59e65..13173642a 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -296,9 +296,14 @@ class ConfirmationForm(forms.Form, BootstrapMixin): class BulkEditForm(forms.Form): - def __init__(self, *args, **kwargs): + def __init__(self, model, *args, **kwargs): super(BulkEditForm, self).__init__(*args, **kwargs) - self.nullable_fields = getattr(self.Meta, 'nullable_fields') + self.model = model + # Copy any nullable fields defined in Meta + if hasattr(self.Meta, 'nullable_fields'): + self.nullable_fields = [field for field in self.Meta.nullable_fields] + else: + self.nullable_fields = [] class BulkImportForm(forms.Form): diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index d0ac4e26a..4e3637ee0 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -301,10 +301,7 @@ class BulkEditView(View): pk_list = [int(pk) for pk in request.POST.getlist('pk')] if '_apply' in request.POST: - if hasattr(self.form, 'custom_fields'): - form = self.form(self.cls, request.POST) - else: - form = self.form(request.POST) + form = self.form(self.cls, request.POST) if form.is_valid(): custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else [] @@ -322,7 +319,7 @@ class BulkEditView(View): # Update custom fields for objects if custom_fields: - objs_updated = self.update_custom_fields(pk_list, form, custom_fields) + objs_updated = self.update_custom_fields(pk_list, form, custom_fields, nullified_fields) if objs_updated and not updated_count: updated_count = objs_updated @@ -333,10 +330,7 @@ class BulkEditView(View): return redirect(redirect_url) else: - if hasattr(self.form, 'custom_fields'): - form = self.form(self.cls, initial={'pk': pk_list}) - else: - form = self.form(initial={'pk': pk_list}) + form = self.form(self.cls, initial={'pk': pk_list}) selected_objects = self.cls.objects.filter(pk__in=pk_list) if not selected_objects: @@ -349,14 +343,23 @@ class BulkEditView(View): 'cancel_url': redirect_url, }) - def update_custom_fields(self, pk_list, form, fields): + def update_custom_fields(self, pk_list, form, fields, nullified_fields): obj_type = ContentType.objects.get_for_model(self.cls) objs_updated = False for name in fields: - if form.cleaned_data[name] not in [None, u'']: - field = form.fields[name].model + field = form.fields[name].model + + # Setting the field to null + if name in form.nullable_fields and name in nullified_fields: + + # Delete all CustomFieldValues for instances of this field belonging to the selected objects. + CustomFieldValue.objects.filter(field=field, obj_type=obj_type, obj_id__in=pk_list).delete() + objs_updated = True + + # Updating the value of the field + elif form.cleaned_data[name] not in [None, u'']: # Check for zero value (bulk editing) if isinstance(form.fields[name], TypedChoiceField) and form.cleaned_data[name] == 0: