mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 17:08:41 -06:00
#353: Allow bulk editing of interfaces
This commit is contained in:
parent
5a4ccbc066
commit
0da3661ff0
@ -16,9 +16,9 @@ from utilities.forms import (
|
|||||||
from .models import (
|
from .models import (
|
||||||
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED,
|
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED,
|
||||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType,
|
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType,
|
||||||
Interface, IFACE_FF_VIRTUAL, InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet,
|
Interface, IFACE_FF_CHOICES, IFACE_FF_VIRTUAL, InterfaceConnection, InterfaceTemplate, Manufacturer, Module,
|
||||||
PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackRole,
|
Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES,
|
||||||
Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD
|
Rack, RackGroup, RackRole, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1008,6 +1008,15 @@ class InterfaceBulkCreateForm(InterfaceCreateForm, BootstrapMixin):
|
|||||||
pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput)
|
pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
|
form_factor = forms.ChoiceField(choices=add_blank_choice(IFACE_FF_CHOICES), required=False)
|
||||||
|
description = forms.CharField(max_length=100, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = ['description']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Interface connections
|
# Interface connections
|
||||||
#
|
#
|
||||||
|
@ -3,10 +3,6 @@ from django.conf.urls import url
|
|||||||
from secrets.views import secret_add
|
from secrets.views import secret_add
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from .models import (
|
|
||||||
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, PowerPortTemplate, PowerOutletTemplate,
|
|
||||||
InterfaceTemplate,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -159,6 +155,7 @@ urlpatterns = [
|
|||||||
# Interfaces
|
# Interfaces
|
||||||
url(r'^devices/interfaces/add/$', views.InterfaceBulkAddView.as_view(), name='interface_add_multi'),
|
url(r'^devices/interfaces/add/$', views.InterfaceBulkAddView.as_view(), name='interface_add_multi'),
|
||||||
url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.interface_add, name='interface_add'),
|
url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.interface_add, name='interface_add'),
|
||||||
|
url(r'^devices/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||||
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||||
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
||||||
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
||||||
|
@ -1425,6 +1425,14 @@ class InterfaceBulkAddView(PermissionRequiredMixin, BulkEditView):
|
|||||||
len(selected_devices)))
|
len(selected_devices)))
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_interface'
|
||||||
|
cls = Interface
|
||||||
|
parent_cls = Device
|
||||||
|
form = forms.InterfaceBulkEditForm
|
||||||
|
template_name = 'dcim/interface_bulk_edit.html'
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_interface'
|
permission_required = 'dcim.delete_interface'
|
||||||
cls = Interface
|
cls = Interface
|
||||||
|
@ -355,7 +355,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if interfaces or device.device_type.is_network_device %}
|
{% if interfaces or device.device_type.is_network_device %}
|
||||||
{% if perms.dcim.delete_interface %}
|
{% if perms.dcim.delete_interface %}
|
||||||
<form method="post" action="{% url 'dcim:interface_bulk_delete' pk=device.pk %}">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
@ -380,8 +380,13 @@
|
|||||||
</table>
|
</table>
|
||||||
{% if perms.dcim.add_interface or perms.dcim.delete_interface %}
|
{% if perms.dcim.add_interface or perms.dcim.delete_interface %}
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
|
{% if interfaces and perms.dcim.change_interface %}
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' pk=device.pk %}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Edit selected
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
{% if interfaces and perms.dcim.delete_interface %}
|
{% if interfaces and perms.dcim.delete_interface %}
|
||||||
<button type="submit" class="btn btn-danger btn-xs">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' pk=device.pk %}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
17
netbox/templates/dcim/interface_bulk_edit.html
Normal file
17
netbox/templates/dcim/interface_bulk_edit.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{% extends 'utilities/bulk_edit_form.html' %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block title %}Interface Bulk Edit{% endblock %}
|
||||||
|
|
||||||
|
{% block selected_objects_table %}
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Form Factor</th>
|
||||||
|
</tr>
|
||||||
|
{% for iface in selected_objects %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ iface.name }}</td>
|
||||||
|
<td>{{ iface.get_form_factor_display }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
@ -34,7 +34,7 @@ def add_blank_choice(choices):
|
|||||||
"""
|
"""
|
||||||
Add a blank choice to the beginning of a choices list.
|
Add a blank choice to the beginning of a choices list.
|
||||||
"""
|
"""
|
||||||
return ((None, '---------'),) + choices
|
return ((None, '---------'),) + tuple(choices)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -280,21 +280,34 @@ class BulkImportView(View):
|
|||||||
|
|
||||||
class BulkEditView(View):
|
class BulkEditView(View):
|
||||||
cls = None
|
cls = None
|
||||||
|
parent_cls = None
|
||||||
form = None
|
form = None
|
||||||
template_name = None
|
template_name = None
|
||||||
default_redirect_url = None
|
default_redirect_url = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self):
|
||||||
return redirect(self.default_redirect_url)
|
return redirect(self.default_redirect_url)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, **kwargs):
|
||||||
|
|
||||||
|
# Attempt to derive parent object if a parent class has been given
|
||||||
|
if self.parent_cls:
|
||||||
|
parent_obj = get_object_or_404(self.parent_cls, **kwargs)
|
||||||
|
else:
|
||||||
|
parent_obj = None
|
||||||
|
|
||||||
|
# Determine URL to redirect users upon modification of objects
|
||||||
posted_redirect_url = request.POST.get('redirect_url')
|
posted_redirect_url = request.POST.get('redirect_url')
|
||||||
if posted_redirect_url and is_safe_url(url=posted_redirect_url, host=request.get_host()):
|
if posted_redirect_url and is_safe_url(url=posted_redirect_url, host=request.get_host()):
|
||||||
redirect_url = posted_redirect_url
|
redirect_url = posted_redirect_url
|
||||||
else:
|
elif parent_obj:
|
||||||
|
redirect_url = parent_obj.get_absolute_url()
|
||||||
|
elif self.default_redirect_url:
|
||||||
redirect_url = reverse(self.default_redirect_url)
|
redirect_url = reverse(self.default_redirect_url)
|
||||||
|
else:
|
||||||
|
raise ImproperlyConfigured('No redirect URL has been provided.')
|
||||||
|
|
||||||
|
# Are we editing *all* objects in the queryset or just a selected subset?
|
||||||
if request.POST.get('_all'):
|
if request.POST.get('_all'):
|
||||||
pk_list = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
|
pk_list = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
|
||||||
else:
|
else:
|
||||||
@ -398,7 +411,7 @@ class BulkDeleteView(View):
|
|||||||
template_name = 'utilities/confirm_bulk_delete.html'
|
template_name = 'utilities/confirm_bulk_delete.html'
|
||||||
default_redirect_url = None
|
default_redirect_url = None
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, **kwargs):
|
||||||
|
|
||||||
# Attempt to derive parent object if a parent class has been given
|
# Attempt to derive parent object if a parent class has been given
|
||||||
if self.parent_cls:
|
if self.parent_cls:
|
||||||
@ -419,9 +432,9 @@ class BulkDeleteView(View):
|
|||||||
|
|
||||||
# Are we deleting *all* objects in the queryset or just a selected subset?
|
# Are we deleting *all* objects in the queryset or just a selected subset?
|
||||||
if request.POST.get('_all'):
|
if request.POST.get('_all'):
|
||||||
pk_list = [x for x in request.POST.get('pk_all').split(',') if x]
|
pk_list = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
|
||||||
else:
|
else:
|
||||||
pk_list = request.POST.getlist('pk')
|
pk_list = [int(pk) for pk in request.POST.getlist('pk')]
|
||||||
|
|
||||||
form_cls = self.get_form()
|
form_cls = self.get_form()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user