mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #1052: Added rack reservation list and bulk delete views
This commit is contained in:
@ -147,6 +147,33 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
|
|
||||||
|
|
||||||
class RackReservationFilter(django_filters.FilterSet):
|
class RackReservationFilter(django_filters.FilterSet):
|
||||||
|
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||||
|
q = django_filters.CharFilter(
|
||||||
|
method='search',
|
||||||
|
label='Search',
|
||||||
|
)
|
||||||
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='rack__site',
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
label='Site (ID)',
|
||||||
|
)
|
||||||
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
name='rack__site__slug',
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site (slug)',
|
||||||
|
)
|
||||||
|
group_id = NullableModelMultipleChoiceFilter(
|
||||||
|
name='rack__group',
|
||||||
|
queryset=RackGroup.objects.all(),
|
||||||
|
label='Group (ID)',
|
||||||
|
)
|
||||||
|
group = NullableModelMultipleChoiceFilter(
|
||||||
|
name='rack__group',
|
||||||
|
queryset=RackGroup.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Group',
|
||||||
|
)
|
||||||
rack_id = django_filters.ModelMultipleChoiceFilter(
|
rack_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
name='rack',
|
name='rack',
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
@ -157,6 +184,16 @@ class RackReservationFilter(django_filters.FilterSet):
|
|||||||
model = RackReservation
|
model = RackReservation
|
||||||
fields = ['rack', 'user']
|
fields = ['rack', 'user']
|
||||||
|
|
||||||
|
def search(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(
|
||||||
|
Q(rack__name__icontains=value) |
|
||||||
|
Q(rack__facility_id__icontains=value) |
|
||||||
|
Q(user__username__icontains=value) |
|
||||||
|
Q(description__icontains=value)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||||
id__in = NumericInFilter(name='id', lookup_expr='in')
|
id__in = NumericInFilter(name='id', lookup_expr='in')
|
||||||
|
@ -330,6 +330,19 @@ class RackReservationForm(BootstrapMixin, forms.ModelForm):
|
|||||||
return unit_choices
|
return unit_choices
|
||||||
|
|
||||||
|
|
||||||
|
class RackReservationFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
q = forms.CharField(required=False, label='Search')
|
||||||
|
site = FilterChoiceField(
|
||||||
|
queryset=Site.objects.annotate(filter_count=Count('racks__reservations')),
|
||||||
|
to_field_name='slug'
|
||||||
|
)
|
||||||
|
group_id = FilterChoiceField(
|
||||||
|
queryset=RackGroup.objects.select_related('site').annotate(filter_count=Count('racks__reservations')),
|
||||||
|
label='Rack group',
|
||||||
|
null_option=(0, 'None')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Manufacturers
|
# Manufacturers
|
||||||
#
|
#
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from itertools import count, groupby
|
||||||
|
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
|
||||||
@ -571,6 +572,15 @@ class RackReservation(models.Model):
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_list(self):
|
||||||
|
"""
|
||||||
|
Express the assigned units as a string of summarized ranges. For example:
|
||||||
|
[0, 1, 2, 10, 14, 15, 16] => "0-2, 10, 14-16"
|
||||||
|
"""
|
||||||
|
group = (list(x) for _, x in groupby(sorted(self.units), lambda x, c=count(): next(c) - x))
|
||||||
|
return ', '.join('-'.join(map(str, (g[0], g[-1])[:len(g)])) for g in group)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device Types
|
# Device Types
|
||||||
|
@ -6,7 +6,7 @@ from utilities.tables import BaseTable, ToggleColumn
|
|||||||
from .models import (
|
from .models import (
|
||||||
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
|
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
|
||||||
Interface, InterfaceTemplate, Manufacturer, Platform, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
Interface, InterfaceTemplate, Manufacturer, Platform, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||||
RackGroup, Region, Site,
|
RackGroup, RackReservation, Region, Site,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -64,6 +64,12 @@ RACK_ROLE = """
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
RACKRESERVATION_ACTIONS = """
|
||||||
|
{% if perms.dcim.change_rackreservation %}
|
||||||
|
<a href="{% url 'dcim:rackreservation_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
||||||
|
{% endif %}
|
||||||
|
"""
|
||||||
|
|
||||||
DEVICEROLE_ACTIONS = """
|
DEVICEROLE_ACTIONS = """
|
||||||
{% if perms.dcim.change_devicerole %}
|
{% if perms.dcim.change_devicerole %}
|
||||||
<a href="{% url 'dcim:devicerole_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
<a href="{% url 'dcim:devicerole_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
||||||
@ -226,6 +232,23 @@ class RackImportTable(BaseTable):
|
|||||||
fields = ('site', 'group', 'name', 'facility_id', 'tenant', 'u_height')
|
fields = ('site', 'group', 'name', 'facility_id', 'tenant', 'u_height')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rack reservations
|
||||||
|
#
|
||||||
|
|
||||||
|
class RackReservationTable(BaseTable):
|
||||||
|
pk = ToggleColumn()
|
||||||
|
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||||
|
unit_list = tables.Column(orderable=False, verbose_name='Units')
|
||||||
|
actions = tables.TemplateColumn(
|
||||||
|
template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = RackReservation
|
||||||
|
fields = ('pk', 'rack', 'unit_list', 'user', 'created', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Manufacturers
|
# Manufacturers
|
||||||
#
|
#
|
||||||
|
@ -36,6 +36,8 @@ urlpatterns = [
|
|||||||
url(r'^rack-roles/(?P<pk>\d+)/edit/$', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
url(r'^rack-roles/(?P<pk>\d+)/edit/$', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||||
|
|
||||||
# Rack reservations
|
# Rack reservations
|
||||||
|
url(r'^rack-reservations/$', views.RackReservationListView.as_view(), name='rackreservation_list'),
|
||||||
|
url(r'^rack-reservations/delete/$', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
|
||||||
url(r'^rack-reservations/(?P<pk>\d+)/edit/$', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
url(r'^rack-reservations/(?P<pk>\d+)/edit/$', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
||||||
url(r'^rack-reservations/(?P<pk>\d+)/delete/$', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
url(r'^rack-reservations/(?P<pk>\d+)/delete/$', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
||||||
|
|
||||||
|
@ -360,6 +360,14 @@ class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
# Rack reservations
|
# Rack reservations
|
||||||
#
|
#
|
||||||
|
|
||||||
|
class RackReservationListView(ObjectListView):
|
||||||
|
queryset = RackReservation.objects.all()
|
||||||
|
filter = filters.RackReservationFilter
|
||||||
|
filter_form = forms.RackReservationFilterForm
|
||||||
|
table = tables.RackReservationTable
|
||||||
|
template_name = 'dcim/rackreservation_list.html'
|
||||||
|
|
||||||
|
|
||||||
class RackReservationEditView(PermissionRequiredMixin, ObjectEditView):
|
class RackReservationEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
permission_required = 'dcim.change_rackreservation'
|
permission_required = 'dcim.change_rackreservation'
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
@ -383,6 +391,12 @@ class RackReservationDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
|||||||
return obj.rack.get_absolute_url()
|
return obj.rack.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
|
class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
|
permission_required = 'dcim.delete_rackreservation'
|
||||||
|
cls = RackReservation
|
||||||
|
default_return_url = 'dcim:rackreservation_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Manufacturers
|
# Manufacturers
|
||||||
#
|
#
|
||||||
|
@ -72,6 +72,8 @@
|
|||||||
{% if perms.dcim.add_rackrole %}
|
{% if perms.dcim.add_rackrole %}
|
||||||
<li><a href="{% url 'dcim:rackrole_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Rack Role</a></li>
|
<li><a href="{% url 'dcim:rackrole_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Rack Role</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li><a href="{% url 'dcim:rackreservation_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Rack Reservations</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown{% if request.path|contains:'/dcim/device,/dcim/manufacturers/,/dcim/platforms/' %} active{% endif %}">
|
<li class="dropdown{% if request.path|contains:'/dcim/device,/dcim/manufacturers/,/dcim/platforms/' %} active{% endif %}">
|
||||||
|
@ -210,7 +210,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% for resv in reservations %}
|
{% for resv in reservations %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ resv.units|join:', ' }}</td>
|
<td>{{ resv.unit_list }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ resv.description }}<br />
|
{{ resv.description }}<br />
|
||||||
<small>{{ resv.user }} · {{ resv.created }}</small>
|
<small>{{ resv.user }} · {{ resv.created }}</small>
|
||||||
|
14
netbox/templates/dcim/rackreservation_list.html
Normal file
14
netbox/templates/dcim/rackreservation_list.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% block title %}Rack Reservations{% endblock %}</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackreservation_bulk_delete' %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
{% include 'inc/search_panel.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Reference in New Issue
Block a user