Skip to content

Commit cbeff30

Browse files
committed
Merge branch 'feature' into verbose-plural-name-slug
2 parents 5c0e7e8 + 95cee68 commit cbeff30

File tree

5 files changed

+136
-33
lines changed

5 files changed

+136
-33
lines changed

netbox_custom_objects/field_types.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from django.utils.translation import gettext_lazy as _
1515
from extras.choices import CustomFieldTypeChoices, CustomFieldUIEditableChoices
1616
from utilities.api import get_serializer_for_model
17-
from utilities.forms.fields import (CSVChoiceField, CSVMultipleChoiceField,
17+
from utilities.forms.fields import (CSVChoiceField, CSVModelChoiceField,
18+
CSVModelMultipleChoiceField, CSVMultipleChoiceField,
1819
DynamicChoiceField,
1920
DynamicMultipleChoiceField, JSONField,
2021
LaxURLField)
@@ -357,7 +358,6 @@ def get_form_field(self, field, for_csv_import=False, **kwargs):
357358
"""
358359
content_type = ContentType.objects.get(pk=field.related_object_type_id)
359360

360-
from utilities.forms.fields import DynamicModelChoiceField
361361
if content_type.app_label == APP_LABEL:
362362
# This is a custom object type
363363
from netbox_custom_objects.models import CustomObjectType
@@ -367,24 +367,34 @@ def get_form_field(self, field, for_csv_import=False, **kwargs):
367367
)
368368
custom_object_type = CustomObjectType.objects.get(pk=custom_object_type_id)
369369
model = custom_object_type.get_model()
370-
field_class = DynamicModelChoiceField
371370
else:
372371
# This is a regular NetBox model
373372
model = content_type.model_class()
374373

374+
if for_csv_import:
375+
field_class = CSVModelChoiceField
376+
# For CSV import, determine to_field_name from the field configuration
377+
to_field_name = getattr(field, 'to_field_name', None) or 'name'
378+
return field_class(
379+
queryset=model.objects.all(),
380+
required=field.required,
381+
initial=field.default,
382+
to_field_name=to_field_name,
383+
)
384+
else:
385+
from utilities.forms.fields import DynamicModelChoiceField
375386
field_class = DynamicModelChoiceField
376-
377-
return field_class(
378-
queryset=model.objects.all(),
379-
required=field.required,
380-
initial=field.default,
381-
query_params=(
382-
field.related_object_filter
383-
if hasattr(field, "related_object_filter")
384-
else None
385-
),
386-
selector=True,
387-
)
387+
return field_class(
388+
queryset=model.objects.all(),
389+
required=field.required,
390+
initial=field.default,
391+
query_params=(
392+
field.related_object_filter
393+
if hasattr(field, "related_object_filter")
394+
else None
395+
),
396+
selector=True,
397+
)
388398

389399
def get_filterform_field(self, field, **kwargs):
390400
return None
@@ -658,19 +668,30 @@ def get_form_field(self, field, for_csv_import=False, **kwargs):
658668
# This is a regular NetBox model
659669
model = content_type.model_class()
660670

661-
from utilities.forms.fields import DynamicModelMultipleChoiceField
662-
663-
return DynamicModelMultipleChoiceField(
664-
queryset=model.objects.all(),
665-
required=field.required,
666-
initial=field.default,
667-
query_params=(
668-
field.related_object_filter
669-
if hasattr(field, "related_object_filter")
670-
else None
671-
),
672-
selector=True,
673-
)
671+
if for_csv_import:
672+
field_class = CSVModelMultipleChoiceField
673+
# For CSV import, determine to_field_name from the field configuration
674+
to_field_name = getattr(field, 'to_field_name', None) or 'name'
675+
return field_class(
676+
queryset=model.objects.all(),
677+
required=field.required,
678+
initial=field.default,
679+
to_field_name=to_field_name,
680+
)
681+
else:
682+
from utilities.forms.fields import DynamicModelMultipleChoiceField
683+
field_class = DynamicModelMultipleChoiceField
684+
return field_class(
685+
queryset=model.objects.all(),
686+
required=field.required,
687+
initial=field.default,
688+
query_params=(
689+
field.related_object_filter
690+
if hasattr(field, "related_object_filter")
691+
else None
692+
),
693+
selector=True,
694+
)
674695

675696
def get_filterform_field(self, field, **kwargs):
676697
return None

netbox_custom_objects/templates/netbox_custom_objects/custom_object_list.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{% plugin_list_buttons model %}
88
{% block extra_controls %}{% endblock %}
99
{% custom_object_add_button model custom_object_type %}
10-
{# {% custom_object_import_button model %} #}
10+
{% custom_object_import_button model custom_object_type %}
1111
{% custom_object_export_button model %}
1212
</div>
1313
{% endblock controls %}

netbox_custom_objects/templatetags/custom_object_buttons.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,12 @@ def custom_object_add_button(model, custom_object_type, action="add"):
203203

204204

205205
@register.inclusion_tag("buttons/import.html")
206-
def custom_object_import_button(model, action="bulk_import"):
206+
def custom_object_import_button(model, custom_object_type, action="bulk_import"):
207207
try:
208-
url = reverse(get_viewname(model, action))
208+
viewname = get_viewname(model, action)
209+
url = reverse(
210+
viewname, kwargs={"custom_object_type": custom_object_type.name.lower()}
211+
)
209212
except NoReverseMatch:
210213
url = None
211214

netbox_custom_objects/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
views.CustomObjectBulkDeleteView.as_view(),
5050
name="customobject_bulk_delete",
5151
),
52+
path(
53+
"<str:custom_object_type>/bulk-import/",
54+
views.CustomObjectBulkImportView.as_view(),
55+
name="customobject_bulk_import",
56+
),
5257
path(
5358
"<str:custom_object_type>/<int:pk>/",
5459
views.CustomObjectView.as_view(),

netbox_custom_objects/views.py

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from extras.models import JournalEntry
1515
from extras.tables import JournalEntryTable
1616
from netbox.filtersets import BaseFilterSet
17-
from netbox.forms import NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
17+
from netbox.forms import (
18+
NetBoxModelBulkEditForm,
19+
NetBoxModelFilterSetForm,
20+
NetBoxModelImportForm,
21+
)
1822
from netbox.views import generic
1923
from netbox.views.generic.mixins import TableMixin
2024
from utilities.forms import ConfirmationForm
@@ -586,8 +590,17 @@ def get_queryset(self, request):
586590
return model.objects.all()
587591

588592
def get_form(self, queryset):
593+
meta = type(
594+
"Meta",
595+
(),
596+
{
597+
"model": queryset.model,
598+
"fields": "__all__",
599+
},
600+
)
601+
589602
attrs = {
590-
"model": queryset.model,
603+
"Meta": meta,
591604
"__module__": "database.forms",
592605
}
593606

@@ -632,6 +645,67 @@ def get_queryset(self, request):
632645
return model.objects.all()
633646

634647

648+
@register_model_view(CustomObject, "bulk_import", path="import", detail=False)
649+
class CustomObjectBulkImportView(generic.BulkImportView):
650+
queryset = None
651+
model_form = None
652+
653+
def get(self, request, custom_object_type):
654+
# Necessary because get() in BulkImportView only takes request and no **kwargs
655+
return super().get(request)
656+
657+
def post(self, request, custom_object_type):
658+
# Necessary because post() in BulkImportView only takes request and no **kwargs
659+
return super().post(request)
660+
661+
def setup(self, request, *args, **kwargs):
662+
super().setup(request, *args, **kwargs)
663+
self.queryset = self.get_queryset(request)
664+
self.model_form = self.get_model_form(self.queryset)
665+
666+
def get_queryset(self, request):
667+
if self.queryset:
668+
return self.queryset
669+
custom_object_type = self.kwargs.get("custom_object_type", None)
670+
self.custom_object_type = CustomObjectType.objects.get(
671+
name__iexact=custom_object_type
672+
)
673+
model = self.custom_object_type.get_model()
674+
return model.objects.all()
675+
676+
def get_model_form(self, queryset):
677+
meta = type(
678+
"Meta",
679+
(),
680+
{
681+
"model": queryset.model,
682+
"fields": "__all__",
683+
},
684+
)
685+
686+
attrs = {
687+
"Meta": meta,
688+
"__module__": "database.forms",
689+
}
690+
691+
for field in self.custom_object_type.fields.all():
692+
field_type = field_types.FIELD_TYPE_CLASS[field.type]()
693+
try:
694+
attrs[field.name] = field_type.get_annotated_form_field(
695+
field, for_csv_import=True
696+
)
697+
except NotImplementedError:
698+
print(f"bulk import form: {field.name} field is not supported")
699+
700+
form = type(
701+
f"{queryset.model._meta.object_name}BulkImportForm",
702+
(NetBoxModelImportForm,),
703+
attrs,
704+
)
705+
706+
return form
707+
708+
635709
class CustomObjectJournalView(ConditionalLoginRequiredMixin, View):
636710
"""
637711
Custom journal view for CustomObject instances.

0 commit comments

Comments
 (0)