1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Added bulk editing capability for custom fields

This commit is contained in:
Jeremy Stretch
2016-08-16 14:57:04 -04:00
parent a9a55350df
commit 7d879bb0dc
11 changed files with 143 additions and 227 deletions

View File

@ -66,15 +66,6 @@ class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'circuits/provider_bulk_edit.html' template_name = 'circuits/provider_bulk_edit.html'
default_redirect_url = 'circuits:provider_list' default_redirect_url = 'circuits:provider_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_provider' permission_required = 'circuits.delete_provider'
@ -159,19 +150,6 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'circuits/circuit_bulk_edit.html' template_name = 'circuits/circuit_bulk_edit.html'
default_redirect_url = 'circuits:circuit_list' default_redirect_url = 'circuits:circuit_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
if form.cleaned_data['tenant'] == 0:
fields_to_update['tenant'] = None
elif form.cleaned_data['tenant']:
fields_to_update['tenant'] = form.cleaned_data['tenant']
for field in ['type', 'provider', 'port_speed', 'commit_rate', 'comments']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_circuit' permission_required = 'circuits.delete_circuit'

View File

@ -3,7 +3,7 @@ import re
from django import forms from django import forms
from django.db.models import Count, Q from django.db.models import Count, Q
from extras.forms import CustomFieldForm from extras.forms import CustomFieldForm, CustomFieldBulkEditForm
from ipam.models import IPAddress from ipam.models import IPAddress
from tenancy.forms import bulkedit_tenant_choices from tenancy.forms import bulkedit_tenant_choices
from tenancy.models import Tenant from tenancy.models import Tenant
@ -112,7 +112,7 @@ class SiteImportForm(BulkImportForm, BootstrapMixin):
csv = CSVDataField(csv_form=SiteFromCSVForm) csv = CSVDataField(csv_form=SiteFromCSVForm)
class SiteBulkEditForm(forms.Form, BootstrapMixin): class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=Site.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Site.objects.all(), widget=forms.MultipleHiddenInput)
tenant = forms.TypedChoiceField(choices=bulkedit_tenant_choices, coerce=int, required=False, label='Tenant') tenant = forms.TypedChoiceField(choices=bulkedit_tenant_choices, coerce=int, required=False, label='Tenant')

View File

@ -122,16 +122,6 @@ class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'dcim/site_bulk_edit.html' template_name = 'dcim/site_bulk_edit.html'
default_redirect_url = 'dcim:site_list' default_redirect_url = 'dcim:site_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
if form.cleaned_data['tenant'] == 0:
fields_to_update['tenant'] = None
elif form.cleaned_data['tenant']:
fields_to_update['tenant'] = form.cleaned_data['tenant']
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
# #
# Rack groups # Rack groups
@ -248,20 +238,6 @@ class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'dcim/rack_bulk_edit.html' template_name = 'dcim/rack_bulk_edit.html'
default_redirect_url = 'dcim:rack_list' default_redirect_url = 'dcim:rack_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['group', 'tenant', 'role']:
if form.cleaned_data[field] == 0:
fields_to_update[field] = None
elif form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
for field in ['site', 'type', 'width', 'u_height', 'comments']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_rack' permission_required = 'dcim.delete_rack'
@ -372,15 +348,6 @@ class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'dcim/devicetype_bulk_edit.html' template_name = 'dcim/devicetype_bulk_edit.html'
default_redirect_url = 'dcim:devicetype_list' default_redirect_url = 'dcim:devicetype_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['manufacturer', 'u_height']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_devicetype' permission_required = 'dcim.delete_devicetype'
@ -682,23 +649,6 @@ class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'dcim/device_bulk_edit.html' template_name = 'dcim/device_bulk_edit.html'
default_redirect_url = 'dcim:device_list' default_redirect_url = 'dcim:device_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['tenant', 'platform']:
if form.cleaned_data[field] == 0:
fields_to_update[field] = None
elif form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
if form.cleaned_data['status']:
status = form.cleaned_data['status']
fields_to_update['status'] = True if status == 'True' else False
for field in ['tenant', 'device_type', 'device_role', 'serial']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_device' permission_required = 'dcim.delete_device'

View File

@ -4,78 +4,90 @@ from django.contrib.contenttypes.models import ContentType
from .models import CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CustomField, CustomFieldValue from .models import CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CustomField, CustomFieldValue
def get_custom_fields_for_model(content_type, bulk_editing=False):
"""Retrieve all CustomFields applicable to the given ContentType"""
field_dict = {}
custom_fields = CustomField.objects.filter(obj_type=content_type)
for cf in custom_fields:
field_name = 'cf_{}'.format(str(cf.name))
# Integer
if cf.type == CF_TYPE_INTEGER:
field = forms.IntegerField(required=cf.required, initial=cf.default)
# Boolean
elif cf.type == CF_TYPE_BOOLEAN:
choices = (
(None, '---------'),
(True, 'True'),
(False, 'False'),
)
field = forms.NullBooleanField(required=cf.required, widget=forms.Select(choices=choices))
# Date
elif cf.type == CF_TYPE_DATE:
field = forms.DateField(required=cf.required, initial=cf.default)
# Select
elif cf.type == CF_TYPE_SELECT:
choices = [(cfc.pk, cfc) for cfc in cf.choices.all()]
if not cf.required:
choices = [(0, 'None')] + choices
if bulk_editing:
choices = [(None, '---------')] + choices
field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required)
else:
field = forms.ModelChoiceField(queryset=cf.choices.all(), required=cf.required)
# Text
else:
field = forms.CharField(max_length=100, required=cf.required, initial=cf.default)
field.model = cf
field.label = cf.label if cf.label else cf.name.capitalize()
field.help_text = cf.description
field_dict[field_name] = field
return field_dict
class CustomFieldForm(forms.ModelForm): class CustomFieldForm(forms.ModelForm):
custom_fields = [] custom_fields = []
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.obj_type = ContentType.objects.get_for_model(self._meta.model)
super(CustomFieldForm, self).__init__(*args, **kwargs) super(CustomFieldForm, self).__init__(*args, **kwargs)
obj_type = ContentType.objects.get_for_model(self._meta.model) # Add all applicable CustomFields to the form
for name, field in get_custom_fields_for_model(self.obj_type).items():
# Find all CustomFields for this model self.fields[name] = field
custom_fields = CustomField.objects.filter(obj_type=obj_type) self.custom_fields.append(name)
for cf in custom_fields:
field_name = 'cf_{}'.format(str(cf.name))
# Integer
if cf.type == CF_TYPE_INTEGER:
field = forms.IntegerField(required=cf.required, initial=cf.default)
# Boolean
elif cf.type == CF_TYPE_BOOLEAN:
if cf.required:
field = forms.BooleanField(required=False, initial=bool(cf.default))
else:
field = forms.NullBooleanField(required=False, initial=bool(cf.default))
# Date
elif cf.type == CF_TYPE_DATE:
field = forms.DateField(required=cf.required, initial=cf.default)
# Select
elif cf.type == CF_TYPE_SELECT:
field = forms.ModelChoiceField(queryset=cf.choices.all(), required=cf.required)
# Text
else:
field = forms.CharField(max_length=100, required=cf.required, initial=cf.default)
field.model = cf
field.label = cf.label if cf.label else cf.name.capitalize()
field.help_text = cf.description
self.fields[field_name] = field
self.custom_fields.append(field_name)
# If editing an existing object, initialize values for all custom fields # If editing an existing object, initialize values for all custom fields
if self.instance.pk: if self.instance.pk:
existing_values = CustomFieldValue.objects.filter(obj_type=obj_type, obj_id=self.instance.pk)\ existing_values = CustomFieldValue.objects.filter(obj_type=self.obj_type, obj_id=self.instance.pk)\
.select_related('field') .select_related('field')
for cfv in existing_values: for cfv in existing_values:
self.initial['cf_{}'.format(str(cfv.field.name))] = cfv.value self.initial['cf_{}'.format(str(cfv.field.name))] = cfv.value
def _save_custom_fields(self): def _save_custom_fields(self):
if self.instance.pk: for field_name in self.custom_fields:
obj_type = ContentType.objects.get_for_model(self.instance) try:
cfv = CustomFieldValue.objects.get(field=self.fields[field_name].model, obj_type=self.obj_type,
for field_name in self.custom_fields: obj_id=self.instance.pk)
except CustomFieldValue.DoesNotExist:
try: cfv = CustomFieldValue(
cfv = CustomFieldValue.objects.get(field=self.fields[field_name].model, obj_type=obj_type, field=self.fields[field_name].model,
obj_id=self.instance.pk) obj_type=self.obj_type,
except CustomFieldValue.DoesNotExist: obj_id=self.instance.pk
cfv = CustomFieldValue( )
field=self.fields[field_name].model, cfv.value = self.cleaned_data[field_name]
obj_type=obj_type, cfv.save()
obj_id=self.instance.pk
)
if cfv.pk and self.cleaned_data[field_name] is None:
cfv.delete()
elif self.cleaned_data[field_name] is not None:
cfv.value = self.cleaned_data[field_name]
cfv.save()
def save(self, commit=True): def save(self, commit=True):
obj = super(CustomFieldForm, self).save(commit) obj = super(CustomFieldForm, self).save(commit)
@ -87,3 +99,19 @@ class CustomFieldForm(forms.ModelForm):
self.save_custom_fields = self._save_custom_fields self.save_custom_fields = self._save_custom_fields
return obj return obj
class CustomFieldBulkEditForm(forms.Form):
custom_fields = []
def __init__(self, model, *args, **kwargs):
self.obj_type = ContentType.objects.get_for_model(model)
super(CustomFieldBulkEditForm, self).__init__(*args, **kwargs)
# Add all applicable CustomFields to the form
for name, field in get_custom_fields_for_model(self.obj_type, bulk_editing=True).items():
field.required = False
self.fields[name] = field
self.custom_fields.append(name)

View File

@ -131,14 +131,22 @@ class CustomFieldValue(models.Model):
if self.field.type == CF_TYPE_INTEGER: if self.field.type == CF_TYPE_INTEGER:
self.val_int = value self.val_int = value
elif self.field.type == CF_TYPE_BOOLEAN: elif self.field.type == CF_TYPE_BOOLEAN:
self.val_int = bool(value) if value else None self.val_int = int(bool(value)) if value is not None else None
elif self.field.type == CF_TYPE_DATE: elif self.field.type == CF_TYPE_DATE:
self.val_date = value self.val_date = value
elif self.field.type == CF_TYPE_SELECT: elif self.field.type == CF_TYPE_SELECT:
self.val_int = value.id # Could be ModelChoiceField or TypedChoiceField
self.val_int = value.id if hasattr(value, 'id') else value
else: else:
self.val_char = value self.val_char = value
def save(self, *args, **kwargs):
if (self.field.type == CF_TYPE_TEXT and self.value == '') or self.value is None:
if self.pk:
self.delete()
else:
super(CustomFieldValue, self).save(*args, **kwargs)
class CustomFieldChoice(models.Model): class CustomFieldChoice(models.Model):
field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT}, field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT},

View File

@ -136,19 +136,6 @@ class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'ipam/vrf_bulk_edit.html' template_name = 'ipam/vrf_bulk_edit.html'
default_redirect_url = 'ipam:vrf_list' default_redirect_url = 'ipam:vrf_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
if form.cleaned_data['tenant'] == 0:
fields_to_update['tenant'] = None
elif form.cleaned_data['tenant']:
fields_to_update['tenant'] = form.cleaned_data['tenant']
for field in ['description']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_vrf' permission_required = 'ipam.delete_vrf'
@ -261,15 +248,6 @@ class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'ipam/aggregate_bulk_edit.html' template_name = 'ipam/aggregate_bulk_edit.html'
default_redirect_url = 'ipam:aggregate_list' default_redirect_url = 'ipam:aggregate_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['rir', 'date_added', 'description']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_aggregate' permission_required = 'ipam.delete_aggregate'
@ -401,20 +379,6 @@ class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'ipam/prefix_bulk_edit.html' template_name = 'ipam/prefix_bulk_edit.html'
default_redirect_url = 'ipam:prefix_list' default_redirect_url = 'ipam:prefix_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['vrf', 'tenant']:
if form.cleaned_data[field] == 0:
fields_to_update[field] = None
elif form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
for field in ['site', 'status', 'role', 'description']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_prefix' permission_required = 'ipam.delete_prefix'
@ -527,20 +491,6 @@ class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'ipam/ipaddress_bulk_edit.html' template_name = 'ipam/ipaddress_bulk_edit.html'
default_redirect_url = 'ipam:ipaddress_list' default_redirect_url = 'ipam:ipaddress_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['vrf', 'tenant']:
if form.cleaned_data[field] == 0:
fields_to_update[field] = None
elif form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
for field in ['description']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_ipaddress' permission_required = 'ipam.delete_ipaddress'
@ -629,19 +579,6 @@ class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'ipam/vlan_bulk_edit.html' template_name = 'ipam/vlan_bulk_edit.html'
default_redirect_url = 'ipam:vlan_list' default_redirect_url = 'ipam:vlan_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
if form.cleaned_data['tenant'] == 0:
fields_to_update['tenant'] = None
elif form.cleaned_data['tenant']:
fields_to_update['tenant'] = form.cleaned_data['tenant']
for field in ['site', 'group', 'status', 'role', 'description']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_vlan' permission_required = 'ipam.delete_vlan'

View File

@ -205,15 +205,6 @@ class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'secrets/secret_bulk_edit.html' template_name = 'secrets/secret_bulk_edit.html'
default_redirect_url = 'secrets:secret_list' default_redirect_url = 'secrets:secret_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
for field in ['role', 'name']:
if form.cleaned_data[field]:
fields_to_update[field] = form.cleaned_data[field]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'secrets.delete_secret' permission_required = 'secrets.delete_secret'

View File

@ -6,7 +6,7 @@
{% block select_objects_table %} {% block select_objects_table %}
{% for site in selected_objects %} {% for site in selected_objects %}
<tr> <tr>
<td><a href="{% url 'dcim:site' slug=site.slug %}">{{ site.slug }}</a></td> <td><a href="{% url 'dcim:site' slug=site.slug %}">{{ site }}</a></td>
<td>{{ site.tenant }}</td> <td>{{ site.tenant }}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -8,7 +8,7 @@
<tr> <tr>
<td>{{ field }}</td> <td>{{ field }}</td>
<td> <td>
{% if value %} {% if value != None %}
{{ value }} {{ value }}
{% elif field.required %} {% elif field.required %}
<span class="text-warning">Not defined</span> <span class="text-warning">Not defined</span>

View File

@ -107,16 +107,6 @@ class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
template_name = 'tenancy/tenant_bulk_edit.html' template_name = 'tenancy/tenant_bulk_edit.html'
default_redirect_url = 'tenancy:tenant_list' default_redirect_url = 'tenancy:tenant_list'
def update_objects(self, pk_list, form):
fields_to_update = {}
if form.cleaned_data['group'] == 0:
fields_to_update['group'] = None
elif form.cleaned_data['group']:
fields_to_update['group'] = form.cleaned_data['group']
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
class TenantBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class TenantBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'tenancy.delete_tenant' permission_required = 'tenancy.delete_tenant'

View File

@ -7,7 +7,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from django.db.models import ProtectedError from django.db.models import ProtectedError
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, TypedChoiceField
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template import TemplateSyntaxError from django.template import TemplateSyntaxError
@ -15,8 +15,8 @@ from django.utils.decorators import method_decorator
from django.utils.http import is_safe_url from django.utils.http import is_safe_url
from django.views.generic import View from django.views.generic import View
from extras.forms import CustomFieldForm from extras.forms import CustomFieldForm, CustomFieldBulkEditForm
from extras.models import ExportTemplate, UserAction from extras.models import CustomField, CustomFieldValue, ExportTemplate, UserAction
from .error_handlers import handle_protectederror from .error_handlers import handle_protectederror
from .forms import ConfirmationForm from .forms import ConfirmationForm
@ -282,9 +282,22 @@ class BulkEditView(View):
pk_list = request.POST.getlist('pk') pk_list = request.POST.getlist('pk')
if '_apply' in request.POST: if '_apply' in request.POST:
form = self.form(request.POST) if hasattr(self.form, 'custom_fields'):
form = self.form(self.cls, request.POST)
else:
form = self.form(request.POST)
if form.is_valid(): if form.is_valid():
updated_count = self.update_objects(pk_list, form)
custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else []
standard_fields = [field for field in form.fields if field not in custom_fields and field != 'pk']
# Update objects
updated_count = self.update_objects(pk_list, form, standard_fields)
# Update custom fields for objects
if custom_fields:
self.update_custom_fields(pk_list, form, custom_fields)
if updated_count: if updated_count:
msg = u'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural) msg = u'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural)
messages.success(self.request, msg) messages.success(self.request, msg)
@ -292,7 +305,10 @@ class BulkEditView(View):
return redirect(redirect_url) return redirect(redirect_url)
else: else:
form = self.form(initial={'pk': pk_list}) if hasattr(self.form, 'custom_fields'):
form = self.form(self.cls, initial={'pk': pk_list})
else:
form = self.form(initial={'pk': pk_list})
selected_objects = self.cls.objects.filter(pk__in=pk_list) selected_objects = self.cls.objects.filter(pk__in=pk_list)
if not selected_objects: if not selected_objects:
@ -305,11 +321,29 @@ class BulkEditView(View):
'cancel_url': redirect_url, 'cancel_url': redirect_url,
}) })
def update_objects(self, obj_list, form): def update_objects(self, pk_list, form, fields):
""" fields_to_update = {}
This method provides the update logic (must be overridden by subclasses).
""" for name in fields:
raise NotImplementedError() if isinstance(form.fields[name], TypedChoiceField) and form.cleaned_data[name] == 0:
fields_to_update[name] = None
elif form.cleaned_data[name]:
fields_to_update[name] = form.cleaned_data[name]
return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
def update_custom_fields(self, pk_list, form, fields):
obj_type = ContentType.objects.get_for_model(self.cls)
for name in fields:
if form.cleaned_data[name] not in [None, u'']:
for pk in pk_list:
try:
cfv = CustomFieldValue.objects.get(field=form.fields[name].model, obj_type=obj_type, obj_id=pk)
except CustomFieldValue.DoesNotExist:
cfv = CustomFieldValue(field=form.fields[name].model, obj_type=obj_type, obj_id=pk)
cfv.value = form.cleaned_data[name]
cfv.save()
class BulkDeleteView(View): class BulkDeleteView(View):