Add dedicated views for nested group models

This commit is contained in:
Jeremy Stretch 2021-03-26 15:07:29 -04:00
parent b7e44a744d
commit 2820d26a0f
13 changed files with 382 additions and 11 deletions

View File

@ -56,7 +56,7 @@ class Region(NestedGroupModel):
csv_headers = ['name', 'slug', 'parent', 'description']
def get_absolute_url(self):
return "{}?region={}".format(reverse('dcim:site_list'), self.slug)
return reverse('dcim:region', args=[self.pk])
def to_csv(self):
return (
@ -108,7 +108,7 @@ class SiteGroup(NestedGroupModel):
csv_headers = ['name', 'slug', 'parent', 'description']
def get_absolute_url(self):
return "{}?group={}".format(reverse('dcim:site_list'), self.slug)
return reverse('dcim:sitegroup', args=[self.pk])
def to_csv(self):
return (
@ -324,7 +324,7 @@ class Location(NestedGroupModel):
]
def get_absolute_url(self):
return "{}?location_id={}".format(reverse('dcim:rack_list'), self.pk)
return reverse('dcim:location', args=[self.pk])
def to_csv(self):
return (

View File

@ -19,12 +19,14 @@ __all__ = (
#
# Rack groups
# Locations
#
class LocationTable(BaseTable):
pk = ToggleColumn()
name = MPTTColumn()
name = MPTTColumn(
linkify=True
)
site = tables.Column(
linkify=True
)

View File

@ -17,7 +17,9 @@ __all__ = (
class RegionTable(BaseTable):
pk = ToggleColumn()
name = MPTTColumn()
name = MPTTColumn(
linkify=True
)
site_count = tables.Column(
verbose_name='Sites'
)
@ -35,7 +37,9 @@ class RegionTable(BaseTable):
class SiteGroupTable(BaseTable):
pk = ToggleColumn()
name = MPTTColumn()
name = MPTTColumn(
linkify=True
)
site_count = tables.Column(
verbose_name='Sites'
)

View File

@ -14,6 +14,7 @@ urlpatterns = [
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'),
path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
path('regions/<int:pk>/', views.RegionView.as_view(), name='region'),
path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
path('regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
@ -24,6 +25,7 @@ urlpatterns = [
path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'),
path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
path('site-groups/<int:pk>/', views.SiteGroupView.as_view(), name='sitegroup'),
path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
path('site-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='sitegroup_changelog', kwargs={'model': SiteGroup}),
@ -47,6 +49,7 @@ urlpatterns = [
path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'),
path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'),
path('locations/<int:pk>/', views.LocationView.as_view(), name='location'),
path('locations/<int:pk>/edit/', views.LocationEditView.as_view(), name='location_edit'),
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
path('locations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='location_changelog', kwargs={'model': Location}),

View File

@ -112,6 +112,23 @@ class RegionListView(generic.ObjectListView):
table = tables.RegionTable
class RegionView(generic.ObjectView):
queryset = Region.objects.all()
def get_extra_context(self, request, instance):
sites = Site.objects.restrict(request.user, 'view').filter(
region=instance
)
sites_table = tables.SiteTable(sites)
sites_table.columns.hide('region')
paginate_table(sites_table, request)
return {
'sites_table': sites_table,
}
class RegionEditView(generic.ObjectEditView):
queryset = Region.objects.all()
model_form = forms.RegionForm
@ -169,6 +186,23 @@ class SiteGroupListView(generic.ObjectListView):
table = tables.SiteGroupTable
class SiteGroupView(generic.ObjectView):
queryset = SiteGroup.objects.all()
def get_extra_context(self, request, instance):
sites = Site.objects.restrict(request.user, 'view').filter(
group=instance
)
sites_table = tables.SiteTable(sites)
sites_table.columns.hide('group')
paginate_table(sites_table, request)
return {
'sites_table': sites_table,
}
class SiteGroupEditView(generic.ObjectEditView):
queryset = SiteGroup.objects.all()
model_form = forms.SiteGroupForm
@ -291,6 +325,23 @@ class LocationListView(generic.ObjectListView):
table = tables.LocationTable
class LocationView(generic.ObjectView):
queryset = Location.objects.all()
def get_extra_context(self, request, instance):
devices = Device.objects.restrict(request.user, 'view').filter(
location=instance
)
devices_table = tables.DeviceTable(devices)
devices_table.columns.hide('location')
paginate_table(devices_table, request)
return {
'devices_table': devices_table,
}
class LocationEditView(generic.ObjectEditView):
queryset = Location.objects.all()
model_form = forms.LocationForm

View File

@ -0,0 +1,73 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'dcim:location_list' %}">Location</a></li>
{% for location in object.get_ancestors %}
<li><a href="{{ location.get_absolute_url }}">{{ location }}</a></li>
{% endfor %}
<li>{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Location</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<td>Parent</td>
<td>
{% if object.parent %}
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
<tr>
<td>Devices</td>
<td>
<a href="{% url 'dcim:device_list' %}?location_id={{ object.pk }}">{{ devices_table.rows|length }}</a>
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col-md-6">
{% include 'inc/custom_fields_panel.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Devices</strong>
</div>
{% include 'inc/table.html' with table=devices_table %}
{% if perms.dcim.add_device %}
<div class="panel-footer text-right noprint">
<a href="{% url 'dcim:device_add' %}?location={{ object.pk }}" class="btn btn-xs btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device
</a>
</div>
{% endif %}
</div>
{% include 'inc/paginator.html' with paginator=devices_table.paginator page=devices_table.page %}
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'dcim:region_list' %}">Region</a></li>
{% for region in object.get_ancestors %}
<li><a href="{{ region.get_absolute_url }}">{{ region }}</a></li>
{% endfor %}
<li>{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Region</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<td>Parent</td>
<td>
{% if object.parent %}
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
<tr>
<td>Sites</td>
<td>
<a href="{% url 'dcim:site_list' %}?region_id={{ object.pk }}">{{ sites_table.rows|length }}</a>
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col-md-6">
{% include 'inc/custom_fields_panel.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Sites</strong>
</div>
{% include 'inc/table.html' with table=sites_table %}
{% if perms.dcim.add_site %}
<div class="panel-footer text-right noprint">
<a href="{% url 'dcim:site_add' %}?region={{ object.pk }}" class="btn btn-xs btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add site
</a>
</div>
{% endif %}
</div>
{% include 'inc/paginator.html' with paginator=sites_table.paginator page=sites_table.page %}
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'dcim:sitegroup_list' %}">Site Groups</a></li>
{% for sitegroup in object.get_ancestors %}
<li><a href="{{ sitegroup.get_absolute_url }}">{{ sitegroup }}</a></li>
{% endfor %}
<li>{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Site Group</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<td>Parent</td>
<td>
{% if object.parent %}
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
<tr>
<td>Sites</td>
<td>
<a href="{% url 'dcim:site_list' %}?group_id={{ object.pk }}">{{ sites_table.rows|length }}</a>
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col-md-6">
{% include 'inc/custom_fields_panel.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Sites</strong>
</div>
{% include 'inc/table.html' with table=sites_table %}
{% if perms.dcim.add_site %}
<div class="panel-footer text-right noprint">
<a href="{% url 'dcim:site_add' %}?group={{ object.pk }}" class="btn btn-xs btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add site
</a>
</div>
{% endif %}
</div>
{% include 'inc/paginator.html' with paginator=sites_table.paginator page=sites_table.page %}
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% block breadcrumbs %}
<li><a href="{% url 'tenancy:tenantgroup_list' %}">Tenant Groups</a></li>
{% for tenantgroup in object.get_ancestors %}
<li><a href="{{ tenantgroup.get_absolute_url }}">{{ tenantgroup }}</a></li>
{% endfor %}
<li>{{ object }}</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Tenant Group</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<td>Parent</td>
<td>
{% if object.parent %}
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
<tr>
<td>Sites</td>
<td>
<a href="{% url 'tenancy:tenant_list' %}?group_id={{ object.pk }}">{{ tenants_table.rows|length }}</a>
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col-md-6">
{% include 'inc/custom_fields_panel.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Tenants</strong>
</div>
{% include 'inc/table.html' with table=tenants_table %}
{% if perms.tenancy.add_tenant %}
<div class="panel-footer text-right noprint">
<a href="{% url 'tenancy:tenant_add' %}?group={{ object.pk }}" class="btn btn-xs btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add tenant
</a>
</div>
{% endif %}
</div>
{% include 'inc/paginator.html' with paginator=tenants_table.paginator page=tenants_table.page %}
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@ -45,7 +45,7 @@ class TenantGroup(NestedGroupModel):
ordering = ['name']
def get_absolute_url(self):
return "{}?group={}".format(reverse('tenancy:tenant_list'), self.slug)
return reverse('tenancy:tenantgroup', args=[self.pk])
def to_csv(self):
return (

View File

@ -35,7 +35,9 @@ class TenantColumn(tables.TemplateColumn):
class TenantGroupTable(BaseTable):
pk = ToggleColumn()
name = MPTTColumn()
name = MPTTColumn(
linkify=True
)
tenant_count = LinkedCountColumn(
viewname='tenancy:tenant_list',
url_params={'group': 'slug'},

View File

@ -13,6 +13,7 @@ urlpatterns = [
path('tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
path('tenant-groups/edit/', views.TenantGroupBulkEditView.as_view(), name='tenantgroup_bulk_edit'),
path('tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
path('tenant-groups/<int:pk>/', views.TenantGroupView.as_view(), name='tenantgroup'),
path('tenant-groups/<int:pk>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
path('tenant-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='tenantgroup_changelog', kwargs={'model': TenantGroup}),

View File

@ -1,9 +1,8 @@
from django.shortcuts import get_object_or_404, render
from circuits.models import Circuit
from dcim.models import Site, Rack, Device, RackReservation
from ipam.models import IPAddress, Prefix, VLAN, VRF
from netbox.views import generic
from utilities.tables import paginate_table
from virtualization.models import VirtualMachine, Cluster
from . import filters, forms, tables
from .models import Tenant, TenantGroup
@ -24,6 +23,23 @@ class TenantGroupListView(generic.ObjectListView):
table = tables.TenantGroupTable
class TenantGroupView(generic.ObjectView):
queryset = TenantGroup.objects.all()
def get_extra_context(self, request, instance):
tenants = Tenant.objects.restrict(request.user, 'view').filter(
group=instance
)
tenants_table = tables.TenantTable(tenants)
tenants_table.columns.hide('group')
paginate_table(tenants_table, request)
return {
'tenants_table': tenants_table,
}
class TenantGroupEditView(generic.ObjectEditView):
queryset = TenantGroup.objects.all()
model_form = forms.TenantGroupForm