Просмотр исходного кода

Add dedicated views for nested group models

Jeremy Stretch 4 лет назад
Родитель
Сommit
2820d26a0f

+ 3 - 3
netbox/dcim/models/sites.py

@@ -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 (

+ 4 - 2
netbox/dcim/tables/racks.py

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

+ 6 - 2
netbox/dcim/tables/sites.py

@@ -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'
     )

+ 3 - 0
netbox/dcim/urls.py

@@ -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}),

+ 51 - 0
netbox/dcim/views.py

@@ -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

+ 73 - 0
netbox/templates/dcim/location.html

@@ -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 %}

+ 73 - 0
netbox/templates/dcim/region.html

@@ -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 %}

+ 73 - 0
netbox/templates/dcim/sitegroup.html

@@ -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 %}

+ 73 - 0
netbox/templates/tenancy/tenantgroup.html

@@ -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 %}

+ 1 - 1
netbox/tenancy/models.py

@@ -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 (

+ 3 - 1
netbox/tenancy/tables.py

@@ -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'},

+ 1 - 0
netbox/tenancy/urls.py

@@ -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}),

+ 18 - 2
netbox/tenancy/views.py

@@ -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