diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 440c12623..8399b0de6 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -70,6 +70,28 @@ class RegionForm(BootstrapMixin, forms.ModelForm): fields = ['parent', 'name', 'slug'] +class RegionCSVForm(forms.ModelForm): + parent = forms.ModelChoiceField( + queryset=Region.objects.all(), + required=False, + to_field_name='name', + help_text='Name of parent region', + error_messages={ + 'invalid_choice': 'Region not found.', + } + ) + + class Meta: + model = Region + fields = [ + 'name', 'slug', 'parent', + ] + help_texts = { + 'name': 'Region name', + 'slug': 'URL-friendly slug', + } + + # # Sites # diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 8dd11e663..af3387368 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -43,6 +43,10 @@ class Region(MPTTModel): name = models.CharField(max_length=50, unique=True) slug = models.SlugField(unique=True) + csv_headers = [ + 'name', 'slug', 'parent', + ] + class MPTTMeta: order_insertion_by = ['name'] @@ -52,6 +56,13 @@ class Region(MPTTModel): def get_absolute_url(self): return "{}?region={}".format(reverse('dcim:site_list'), self.slug) + def to_csv(self): + return csv_format([ + self.name, + self.slug, + self.parent.name if self.parent else None, + ]) + # # Sites diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 172f634fb..b03b7d030 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -15,6 +15,7 @@ urlpatterns = [ # Regions url(r'^regions/$', views.RegionListView.as_view(), name='region_list'), url(r'^regions/add/$', views.RegionCreateView.as_view(), name='region_add'), + url(r'^regions/import/$', views.RegionBulkImportView.as_view(), name='region_import'), url(r'^regions/delete/$', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'), url(r'^regions/(?P\d+)/edit/$', views.RegionEditView.as_view(), name='region_edit'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index ea07138d5..ca144afe8 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -202,6 +202,13 @@ class RegionEditView(RegionCreateView): permission_required = 'dcim.change_region' +class RegionBulkImportView(PermissionRequiredMixin, BulkImportView): + permission_required = 'dcim.add_region' + model_form = forms.RegionCSVForm + table = tables.RegionTable + default_return_url = 'dcim:region_list' + + class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_region' cls = Region diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index 86da90895..0ebd49af0 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -37,7 +37,8 @@ GRAPH_TYPE_CHOICES = ( # Models which support export templates EXPORTTEMPLATE_MODELS = [ - 'site', 'rack', 'device', 'consoleport', 'powerport', 'interfaceconnection', # DCIM + 'site', 'region', 'rack', 'device', # DCIM + 'consoleport', 'powerport', 'interfaceconnection', # DCIM 'aggregate', 'prefix', 'ipaddress', 'vlan', # IPAM 'provider', 'circuit', # Circuits 'tenant', # Tenants diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 10b4970a8..65cf39b30 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -40,6 +40,7 @@
  • Regions
  • {% if perms.dcim.add_region %}
  • Add a Region
  • +
  • Import Regions
  • {% endif %}
  • Tenants
  • diff --git a/netbox/templates/dcim/region_list.html b/netbox/templates/dcim/region_list.html index b54201a34..f5dde06d8 100644 --- a/netbox/templates/dcim/region_list.html +++ b/netbox/templates/dcim/region_list.html @@ -10,7 +10,12 @@ Add a region + + + Import regions + {% endif %} + {% include 'inc/export_button.html' with obj_type='regions' %}

    {{ block.title }}