diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 28c4d6844..292328d61 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -42,7 +42,7 @@ class ProviderEditView(PermissionRequiredMixin, ObjectEditView): model = Provider form_class = forms.ProviderForm template_name = 'circuits/provider_edit.html' - cancel_url = 'circuits:provider_list' + obj_list_url = 'circuits:provider_list' class ProviderDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -88,8 +88,8 @@ class CircuitTypeEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'circuits.change_circuittype' model = CircuitType form_class = forms.CircuitTypeForm - success_url = 'circuits:circuittype_list' - cancel_url = 'circuits:circuittype_list' + obj_list_url = 'circuits:circuittype_list' + use_obj_view = False class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -126,7 +126,7 @@ class CircuitEditView(PermissionRequiredMixin, ObjectEditView): form_class = forms.CircuitForm fields_initial = ['site'] template_name = 'circuits/circuit_edit.html' - cancel_url = 'circuits:circuit_list' + obj_list_url = 'circuits:circuit_list' class CircuitDeleteView(PermissionRequiredMixin, ObjectDeleteView): diff --git a/netbox/dcim/admin.py b/netbox/dcim/admin.py index 9fd16a87d..8828b52c4 100644 --- a/netbox/dcim/admin.py +++ b/netbox/dcim/admin.py @@ -183,10 +183,14 @@ class DeviceAdmin(admin.ModelAdmin): DeviceBayAdmin, ModuleAdmin, ] - list_display = ['display_name', 'device_type', 'device_role', 'primary_ip', 'rack', 'position', 'asset_tag', + list_display = ['display_name', 'device_type_full_name', 'device_role', 'primary_ip', 'rack', 'position', 'asset_tag', 'serial'] list_filter = ['device_role'] def get_queryset(self, request): qs = super(DeviceAdmin, self).get_queryset(request) return qs.select_related('device_type__manufacturer', 'device_role', 'primary_ip4', 'primary_ip6', 'rack') + + def device_type_full_name(self, obj): + return obj.device_type.full_name + device_type_full_name.short_description = 'Device type' diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 69bccd253..4cf53c303 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -260,8 +260,11 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): ).distinct() def _mac_address(self, queryset, value): + value = value.strip() + if not value: + return queryset try: - return queryset.filter(interfaces__mac_address=value.strip()).distinct() + return queryset.filter(interfaces__mac_address=value).distinct() except AddrFormatError: return queryset.none() diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 23da437a7..961d22440 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -612,7 +612,7 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm): platform = FilterChoiceField(queryset=Platform.objects.annotate(filter_count=Count('devices')), to_field_name='slug', null_option=(0, 'None')) status = forms.NullBooleanField(required=False, widget=forms.Select(choices=FORM_STATUS_CHOICES)) - mac_address = forms.CharField(label='MAC address') + mac_address = forms.CharField(required=False, label='MAC address') # diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index e8967b0c0..a87b778d7 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -561,7 +561,7 @@ class DeviceType(models.Model): ] def __unicode__(self): - return u'{} {}'.format(self.manufacturer, self.model) + return self.model def __init__(self, *args, **kwargs): super(DeviceType, self).__init__(*args, **kwargs) @@ -617,6 +617,10 @@ class DeviceType(models.Model): 'u_height': "Child device types must be 0U." }) + @property + def full_name(self): + return u'{} {}'.format(self.manufacturer.name, self.model) + @property def is_parent_device(self): return bool(self.subdevice_role) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index c81c24f82..94d359ac0 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -294,7 +294,8 @@ class PlatformTable(BaseTable): name = tables.LinkColumn(verbose_name='Name') device_count = tables.Column(verbose_name='Devices') slug = tables.Column(verbose_name='Slug') - actions = tables.TemplateColumn(template_code=PLATFORM_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='') + actions = tables.TemplateColumn(template_code=PLATFORM_ACTIONS, attrs={'td': {'class': 'text-right'}}, + verbose_name='') class Meta(BaseTable.Meta): model = Platform @@ -313,7 +314,8 @@ class DeviceTable(BaseTable): site = tables.Column(accessor=Accessor('rack.site'), verbose_name='Site') rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack') device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role') - device_type = tables.Column(verbose_name='Type') + device_type = tables.LinkColumn('dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type', + text=lambda record: record.device_type.full_name) primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address', template_code="{{ record.primary_ip.address.ip }}") diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index d31670446..d7fc2a88b 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -8,7 +8,6 @@ from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse -from django.db import transaction from django.db.models import Count from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render @@ -99,7 +98,7 @@ class SiteEditView(PermissionRequiredMixin, ObjectEditView): model = Site form_class = forms.SiteForm template_name = 'dcim/site_edit.html' - cancel_url = 'dcim:site_list' + obj_list_url = 'dcim:site_list' class SiteDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -141,8 +140,8 @@ class RackGroupEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_rackgroup' model = RackGroup form_class = forms.RackGroupForm - success_url = 'dcim:rackgroup_list' - cancel_url = 'dcim:rackgroup_list' + obj_list_url = 'dcim:rackgroup_list' + use_obj_view = False class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -166,8 +165,8 @@ class RackRoleEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_rackrole' model = RackRole form_class = forms.RackRoleForm - success_url = 'dcim:rackrole_list' - cancel_url = 'dcim:rackrole_list' + obj_list_url = 'dcim:rackrole_list' + use_obj_view = False class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -214,7 +213,7 @@ class RackEditView(PermissionRequiredMixin, ObjectEditView): model = Rack form_class = forms.RackForm template_name = 'dcim/rack_edit.html' - cancel_url = 'dcim:rack_list' + obj_list_url = 'dcim:rack_list' class RackDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -260,8 +259,8 @@ class ManufacturerEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_manufacturer' model = Manufacturer form_class = forms.ManufacturerForm - success_url = 'dcim:manufacturer_list' - cancel_url = 'dcim:manufacturer_list' + obj_list_url = 'dcim:manufacturer_list' + use_obj_view = False class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -332,7 +331,7 @@ class DeviceTypeEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_devicetype' model = DeviceType form_class = forms.DeviceTypeForm - cancel_url = 'dcim:devicetype_list' + obj_list_url = 'dcim:devicetype_list' class DeviceTypeDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -497,8 +496,8 @@ class DeviceRoleEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_devicerole' model = DeviceRole form_class = forms.DeviceRoleForm - success_url = 'dcim:devicerole_list' - cancel_url = 'dcim:devicerole_list' + obj_list_url = 'dcim:devicerole_list' + use_obj_view = False class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -522,8 +521,8 @@ class PlatformEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_platform' model = Platform form_class = forms.PlatformForm - success_url = 'dcim:platform_list' - cancel_url = 'dcim:platform_list' + obj_list_url = 'dcim:platform_list' + use_obj_view = False class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -617,7 +616,7 @@ class DeviceEditView(PermissionRequiredMixin, ObjectEditView): form_class = forms.DeviceForm fields_initial = ['site', 'rack', 'position', 'face', 'device_bay'] template_name = 'dcim/device_edit.html' - cancel_url = 'dcim:device_list' + obj_list_url = 'dcim:device_list' class DeviceDeleteView(PermissionRequiredMixin, ObjectDeleteView): diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index d7a37dacd..9d738219d 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -49,7 +49,7 @@ def get_custom_fields_for_model(content_type, filterable_only=False, bulk_edit=F # Select elif cf.type == CF_TYPE_SELECT: choices = [(cfc.pk, cfc) for cfc in cf.choices.all()] - if bulk_edit or filterable_only: + if not cf.required or bulk_edit or filterable_only: choices = [(None, '---------')] + choices field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 0be5b225e..0ad11a38c 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from django_tables2 import RequestConfig import netaddr @@ -117,7 +116,7 @@ class VRFEditView(PermissionRequiredMixin, ObjectEditView): model = VRF form_class = forms.VRFForm template_name = 'ipam/vrf_edit.html' - cancel_url = 'ipam:vrf_list' + obj_list_url = 'ipam:vrf_list' class VRFDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -241,8 +240,8 @@ class RIREditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'ipam.change_rir' model = RIR form_class = forms.RIRForm - success_url = 'ipam:rir_list' - cancel_url = 'ipam:rir_list' + obj_list_url = 'ipam:rir_list' + use_obj_view = False class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -307,7 +306,7 @@ class AggregateEditView(PermissionRequiredMixin, ObjectEditView): model = Aggregate form_class = forms.AggregateForm template_name = 'ipam/aggregate_edit.html' - cancel_url = 'ipam:aggregate_list' + obj_list_url = 'ipam:aggregate_list' class AggregateDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -353,8 +352,8 @@ class RoleEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'ipam.change_role' model = Role form_class = forms.RoleForm - success_url = 'ipam:role_list' - cancel_url = 'ipam:role_list' + obj_list_url = 'ipam:role_list' + use_obj_view = False class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -438,7 +437,7 @@ class PrefixEditView(PermissionRequiredMixin, ObjectEditView): form_class = forms.PrefixForm template_name = 'ipam/prefix_edit.html' fields_initial = ['vrf', 'tenant', 'site', 'prefix', 'vlan'] - cancel_url = 'ipam:prefix_list' + obj_list_url = 'ipam:prefix_list' class PrefixDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -602,7 +601,7 @@ class IPAddressEditView(PermissionRequiredMixin, ObjectEditView): form_class = forms.IPAddressForm fields_initial = ['address', 'vrf'] template_name = 'ipam/ipaddress_edit.html' - cancel_url = 'ipam:ipaddress_list' + obj_list_url = 'ipam:ipaddress_list' class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView): @@ -665,8 +664,8 @@ class VLANGroupEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'ipam.change_vlangroup' model = VLANGroup form_class = forms.VLANGroupForm - success_url = 'ipam:vlangroup_list' - cancel_url = 'ipam:vlangroup_list' + obj_list_url = 'ipam:vlangroup_list' + use_obj_view = False class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): @@ -705,7 +704,7 @@ class VLANEditView(PermissionRequiredMixin, ObjectEditView): model = VLAN form_class = forms.VLANForm template_name = 'ipam/vlan_edit.html' - cancel_url = 'ipam:vlan_list' + obj_list_url = 'ipam:vlan_list' class VLANDeleteView(PermissionRequiredMixin, ObjectDeleteView): diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 34d856f2e..f4b0ccc2c 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.7.2-r1' +VERSION = '1.7.3' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: @@ -185,6 +185,8 @@ SECRETS_MIN_PUBKEY_SIZE = 2048 REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',) } +if LOGIN_REQUIRED: + REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES'] = ('rest_framework.permissions.IsAuthenticated',) # Swagger settings (API docs) SWAGGER_SETTINGS = { diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index a99af80b6..7880adfb2 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -30,8 +30,8 @@ class SecretRoleEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'secrets.change_secretrole' model = SecretRole form_class = forms.SecretRoleForm - success_url = 'secrets:secretrole_list' - cancel_url = 'secrets:secretrole_list' + obj_list_url = 'secrets:secretrole_list' + use_obj_view = False class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): diff --git a/netbox/tempfile b/netbox/tempfile deleted file mode 100644 index e69de29bb..000000000 diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 14d5b0869..785938970 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -56,7 +56,7 @@
Model | -{{ device.device_type }} | +{{ device.device_type.full_name }} | ||||||
Serial Number | diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 42cb30f99..0492bd2cc 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -2,7 +2,7 @@ {% load helpers %} {% load render_table from django_tables2 %} -{% block title %}{{ devicetype }}{% endblock %} +{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %} {% block content %}- {{ devicebay.installed_device.device_type }} + {{ devicebay.installed_device.device_type.full_name }} | {% else %}
diff --git a/netbox/templates/dcim/inc/_rack_elevation.html b/netbox/templates/dcim/inc/_rack_elevation.html
index 1ad8cdb57..0ffc6b7ad 100644
--- a/netbox/templates/dcim/inc/_rack_elevation.html
+++ b/netbox/templates/dcim/inc/_rack_elevation.html
@@ -24,7 +24,7 @@
{{ u.device.device_type.full_name }} ({{ u.device.device_type.u_height }}U)"> {{ u.device.name|default:u.device.device_role }} {% if u.device.devicebay_count %} ({{ u.device.get_children.count }}/{{ u.device.devicebay_count }}) diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index af457a21d..f2bafe43b 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -153,8 +153,14 @@ {{ device.name }} |
{{ device.device_role }} | -{{ device.device_type }} | -{% if device.parent_bay %}{{ device.parent_bay }}{% endif %} | +{{ device.device_type.full_name }} | ++ {% if device.parent_bay %} + {{ device.parent_bay }} + {% else %} + N/A + {% endif %} + |