From bc9158a74ff9606da2d5608681b130d97c6fb9a5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 2 Aug 2016 16:04:25 -0400 Subject: [PATCH] Closes #412: Tenant group assignment is no longer mandatory --- docs/data-model/tenancy.md | 15 ++++++++++++- netbox/tenancy/forms.py | 16 ++++++++++++-- .../migrations/0002_tenant_group_optional.py | 21 +++++++++++++++++++ netbox/tenancy/models.py | 2 +- netbox/tenancy/views.py | 7 ++++--- 5 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 netbox/tenancy/migrations/0002_tenant_group_optional.py diff --git a/docs/data-model/tenancy.md b/docs/data-model/tenancy.md index d3e8b8c29..c9cfc997c 100644 --- a/docs/data-model/tenancy.md +++ b/docs/data-model/tenancy.md @@ -4,6 +4,19 @@ NetBox supports the concept of individual tenants within its parent organization A tenant represents a discrete organization. Certain resources within NetBox can be assigned to a tenant. This makes it very convenient to track which resources are assigned to which customers, for instance. +The following objects can be assigned to tenants: + +* Sites +* Racks +* Devices +* VRFs +* Prefixes +* IP addresses +* VLANs +* Circuits + +If a prefix or IP address is not assigned to a tenant, it will appear to inherit the tenant to which its parent VRF is assigned, if any. + ### Tenant Groups -Tenants are grouped by type. For instance, you might create one group called "Customers" and one called "Acquisitions." +Tenants can be grouped by type. For instance, you might create one group called "Customers" and one called "Acquisitions." The assignment of tenants to groups is optional. diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py index bf04454ba..eaf95ab2c 100644 --- a/netbox/tenancy/forms.py +++ b/netbox/tenancy/forms.py @@ -8,6 +8,18 @@ from utilities.forms import ( from .models import Tenant, TenantGroup +def bulkedit_tenantgroup_choices(): + """ + Include an option to remove the currently assigned TenantGroup from a Tenant. + """ + choices = [ + (None, '---------'), + (0, 'None'), + ] + choices += [(g.pk, g.name) for g in TenantGroup.objects.all()] + return choices + + def bulkedit_tenant_choices(): """ Include an option to remove the currently assigned Tenant from an object. @@ -46,7 +58,7 @@ class TenantForm(forms.ModelForm, BootstrapMixin): class TenantFromCSVForm(forms.ModelForm): - group = forms.ModelChoiceField(TenantGroup.objects.all(), to_field_name='name', + group = forms.ModelChoiceField(TenantGroup.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Group not found.'}) class Meta: @@ -60,7 +72,7 @@ class TenantImportForm(BulkImportForm, BootstrapMixin): class TenantBulkEditForm(forms.Form, BootstrapMixin): pk = forms.ModelMultipleChoiceField(queryset=Tenant.objects.all(), widget=forms.MultipleHiddenInput) - group = forms.ModelChoiceField(queryset=TenantGroup.objects.all(), required=False) + group = forms.TypedChoiceField(choices=bulkedit_tenantgroup_choices, coerce=int, required=False, label='Group') def tenant_group_choices(): diff --git a/netbox/tenancy/migrations/0002_tenant_group_optional.py b/netbox/tenancy/migrations/0002_tenant_group_optional.py new file mode 100644 index 000000000..95b1138ac --- /dev/null +++ b/netbox/tenancy/migrations/0002_tenant_group_optional.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.8 on 2016-08-02 19:54 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenancy', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='tenant', + name='group', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tenants', to='tenancy.TenantGroup'), + ), + ] diff --git a/netbox/tenancy/models.py b/netbox/tenancy/models.py index 72bc92cae..6eb903f8b 100644 --- a/netbox/tenancy/models.py +++ b/netbox/tenancy/models.py @@ -28,7 +28,7 @@ class Tenant(CreatedUpdatedModel): """ name = models.CharField(max_length=30, unique=True) slug = models.SlugField(unique=True) - group = models.ForeignKey('TenantGroup', related_name='tenants', on_delete=models.PROTECT) + group = models.ForeignKey('TenantGroup', related_name='tenants', blank=True, null=True, on_delete=models.SET_NULL) description = models.CharField(max_length=100, blank=True, help_text="Long-form name (optional)") comments = models.TextField(blank=True) diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index bab624589..6b655ffd6 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -99,9 +99,10 @@ class TenantBulkEditView(PermissionRequiredMixin, BulkEditView): def update_objects(self, pk_list, form): fields_to_update = {} - for field in ['group']: - if form.cleaned_data[field]: - fields_to_update[field] = form.cleaned_data[field] + if form.cleaned_data['group'] == 0: + fields_to_update['group'] = None + elif form.cleaned_data['group']: + fields_to_update['group'] = form.cleaned_data['group'] return self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)