diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py
index 79cad0a6b..444974240 100644
--- a/netbox/circuits/forms.py
+++ b/netbox/circuits/forms.py
@@ -8,8 +8,8 @@ from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFi
from tenancy.forms import TenancyForm
from tenancy.models import Tenant
from utilities.forms import (
- APISelect, BootstrapMixin, BulkImportForm, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, CSVDataField,
- FilterChoiceField, Livesearch, SmallTextarea, SlugField,
+ APISelect, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, FilterChoiceField,
+ SmallTextarea, SlugField,
)
from .models import Circuit, CircuitTermination, CircuitType, Provider
@@ -39,15 +39,17 @@ class ProviderForm(BootstrapMixin, CustomFieldForm):
}
-class ProviderFromCSVForm(forms.ModelForm):
+class ProviderCSVForm(forms.ModelForm):
+ slug = SlugField()
class Meta:
model = Provider
- fields = ['name', 'slug', 'asn', 'account', 'portal_url']
-
-
-class ProviderImportForm(BootstrapMixin, BulkImportForm):
- csv = CSVDataField(csv_form=ProviderFromCSVForm)
+ fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'comments']
+ help_texts = {
+ 'name': 'Provider name',
+ 'asn': 'Autonomous system number',
+ 'comments': 'Free-form comments'
+ }
class ProviderBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
@@ -102,21 +104,36 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
}
-class CircuitFromCSVForm(forms.ModelForm):
- provider = forms.ModelChoiceField(Provider.objects.all(), to_field_name='name',
- error_messages={'invalid_choice': 'Provider not found.'})
- type = forms.ModelChoiceField(CircuitType.objects.all(), to_field_name='name',
- error_messages={'invalid_choice': 'Invalid circuit type.'})
- tenant = forms.ModelChoiceField(Tenant.objects.all(), to_field_name='name', required=False,
- error_messages={'invalid_choice': 'Tenant not found.'})
+class CircuitCSVForm(forms.ModelForm):
+ provider = forms.ModelChoiceField(
+ queryset=Provider.objects.all(),
+ to_field_name='name',
+ help_text='Name of parent provider',
+ error_messages={
+ 'invalid_choice': 'Provider not found.'
+ }
+ )
+ type = forms.ModelChoiceField(
+ queryset=CircuitType.objects.all(),
+ to_field_name='name',
+ help_text='Name of assigned tenant',
+ error_messages={
+ 'invalid_choice': 'Invalid circuit type.'
+ }
+ )
+ tenant = forms.ModelChoiceField(
+ queryset=Tenant.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text='Name of circuit type',
+ error_messages={
+ 'invalid_choice': 'Tenant not found.'
+ }
+ )
class Meta:
model = Circuit
- fields = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description']
-
-
-class CircuitImportForm(BootstrapMixin, BulkImportForm):
- csv = CSVDataField(csv_form=CircuitFromCSVForm)
+ fields = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments']
class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py
index eed612a33..144007211 100644
--- a/netbox/circuits/views.py
+++ b/netbox/circuits/views.py
@@ -12,7 +12,7 @@ from django.views.generic import View
from extras.models import Graph, GRAPH_TYPE_PROVIDER
from utilities.forms import ConfirmationForm
from utilities.views import (
- BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
+ BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
)
from . import filters, forms, tables
from .models import Circuit, CircuitTermination, CircuitType, Provider, TERM_SIDE_A, TERM_SIDE_Z
@@ -63,11 +63,10 @@ class ProviderDeleteView(PermissionRequiredMixin, ObjectDeleteView):
default_return_url = 'circuits:provider_list'
-class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
+class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView2):
permission_required = 'circuits.add_provider'
- form = forms.ProviderImportForm
+ model_form = forms.ProviderCSVForm
table = tables.ProviderTable
- template_name = 'circuits/provider_import.html'
default_return_url = 'circuits:provider_list'
@@ -161,11 +160,10 @@ class CircuitDeleteView(PermissionRequiredMixin, ObjectDeleteView):
default_return_url = 'circuits:circuit_list'
-class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
+class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView2):
permission_required = 'circuits.add_circuit'
- form = forms.CircuitImportForm
+ model_form = forms.CircuitCSVForm
table = tables.CircuitTable
- template_name = 'circuits/circuit_import.html'
default_return_url = 'circuits:circuit_list'
diff --git a/netbox/templates/circuits/circuit_import.html b/netbox/templates/circuits/circuit_import.html
deleted file mode 100644
index 4b0c40b09..000000000
--- a/netbox/templates/circuits/circuit_import.html
+++ /dev/null
@@ -1,55 +0,0 @@
-{% extends 'utilities/obj_import.html' %}
-
-{% block title %}Circuit Import{% endblock %}
-
-{% block instructions %}
-
CSV Format
-
-
-
- Field |
- Description |
- Example |
-
-
-
-
- Circuit ID |
- Alphanumeric circuit identifier |
- IC-603122 |
-
-
- Provider |
- Name of circuit provider |
- TeliaSonera |
-
-
- Type |
- Circuit type |
- Transit |
-
-
- Tenant |
- Name of tenant (optional) |
- Strickland Propane |
-
-
- Install Date |
- Date in YYYY-MM-DD format (optional) |
- 2016-02-23 |
-
-
- Commit rate |
- Commited rate in Kbps (optional) |
- 2000 |
-
-
- Description |
- Short description (optional) |
- Primary for voice |
-
-
-
- Example
- IC-603122,TeliaSonera,Transit,Strickland Propane,2016-02-23,2000,Primary for voice
-{% endblock %}
diff --git a/netbox/templates/circuits/provider_import.html b/netbox/templates/circuits/provider_import.html
deleted file mode 100644
index 2ab2e5efb..000000000
--- a/netbox/templates/circuits/provider_import.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% extends 'utilities/obj_import.html' %}
-
-{% block title %}Provider Import{% endblock %}
-
-{% block instructions %}
- CSV Format
-
-
-
- Field |
- Description |
- Example |
-
-
-
-
- Name |
- Provider's proper name |
- Level 3 |
-
-
- Slug |
- URL-friendly name |
- level3 |
-
-
- ASN |
- Autonomous system number (optional) |
- 3356 |
-
-
- Account |
- Account number (optional) |
- 08931544 |
-
-
- Portal URL |
- Customer service portal URL (optional) |
- https://mylevel3.net |
-
-
-
- Example
- Level 3,level3,3356,08931544,https://mylevel3.net
-{% endblock %}
diff --git a/netbox/templates/tenancy/tenant_import.html b/netbox/templates/tenancy/tenant_import.html
deleted file mode 100644
index 4d0e97f48..000000000
--- a/netbox/templates/tenancy/tenant_import.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{% extends 'utilities/obj_import.html' %}
-
-{% block title %}Tenant Import{% endblock %}
diff --git a/netbox/templates/utilities/obj_import.html b/netbox/templates/utilities/obj_import.html
index 325cfa304..25b737175 100644
--- a/netbox/templates/utilities/obj_import.html
+++ b/netbox/templates/utilities/obj_import.html
@@ -1,8 +1,9 @@
{% extends '_base.html' %}
+{% load helpers %}
{% load form_helpers %}
{% block content %}
-{% block title %}{% endblock %}
+{% block title %}{{ obj_type|bettertitle }} Import{% endblock %}
{% if form.non_field_errors %}
@@ -40,7 +41,7 @@
{{ name }} |
{% if field.required %}{% endif %} |
- {{ field.help_text }} |
+ {{ field.help_text|default:field.label }} |
{% endfor %}
diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py
index 32e8263c2..9950abfc2 100644
--- a/netbox/tenancy/forms.py
+++ b/netbox/tenancy/forms.py
@@ -36,10 +36,12 @@ class TenantForm(BootstrapMixin, CustomFieldForm):
class TenantCSVForm(forms.ModelForm):
+ slug = SlugField()
group = forms.ModelChoiceField(
queryset=TenantGroup.objects.all(),
required=False,
to_field_name='name',
+ help_text='Name of parent group',
error_messages={
'invalid_choice': 'Group not found.'
}
@@ -48,6 +50,10 @@ class TenantCSVForm(forms.ModelForm):
class Meta:
model = Tenant
fields = ['name', 'slug', 'group', 'description', 'comments']
+ help_texts = {
+ 'name': 'Tenant name',
+ 'comments': 'Free-form comments'
+ }
class TenantBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py
index 27afed269..feaec38be 100644
--- a/netbox/tenancy/views.py
+++ b/netbox/tenancy/views.py
@@ -99,7 +99,6 @@ class TenantBulkImportView(PermissionRequiredMixin, BulkImportView2):
permission_required = 'tenancy.add_tenant'
model_form = forms.TenantCSVForm
table = tables.TenantTable
- template_name = 'tenancy/tenant_import.html'
default_return_url = 'tenancy:tenant_list'
diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py
index fcdaf2c53..6c07aad7f 100644
--- a/netbox/utilities/forms.py
+++ b/netbox/utilities/forms.py
@@ -276,7 +276,9 @@ class CSVDataField2(forms.CharField):
if not self.initial:
self.initial = ','.join(required_fields) + '\n'
if not self.help_text:
- self.help_text = 'Enter one line per record. Use commas to separate values.'
+ self.help_text = 'Enter the list of column headers followed by one line per record to be imported. Use ' \
+ 'commas to separate values. Multi-line data and values containing commas may be wrapped ' \
+ 'in double quotes.'
def to_python(self, value):
diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py
index 7791e69b7..e06b900c0 100644
--- a/netbox/utilities/views.py
+++ b/netbox/utilities/views.py
@@ -435,8 +435,8 @@ class BulkImportView2(View):
"""
model_form = None
table = None
- template_name = None
default_return_url = None
+ template_name = 'utilities/obj_import.html'
def _import_form(self, *args, **kwargs):
@@ -453,6 +453,7 @@ class BulkImportView2(View):
return render(request, self.template_name, {
'form': self._import_form(),
'fields': self.model_form().fields,
+ 'obj_type': self.model_form._meta.model._meta.verbose_name,
'return_url': self.default_return_url,
})
@@ -496,6 +497,7 @@ class BulkImportView2(View):
return render(request, self.template_name, {
'form': form,
'fields': self.model_form().fields,
+ 'obj_type': self.model_form._meta.model._meta.verbose_name,
'return_url': self.default_return_url,
})