bulk_import.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from django import forms
  2. from django.db import models
  3. from django.utils.translation import gettext_lazy as _
  4. from extras.choices import *
  5. from extras.models import CustomField, Tag
  6. from users.models import Owner
  7. from utilities.forms import CSVModelForm
  8. from utilities.forms.fields import CSVModelMultipleChoiceField, CSVModelChoiceField, SlugField
  9. from .model_forms import NetBoxModelForm
  10. __all__ = (
  11. 'NestedGroupModelImportForm',
  12. 'NetBoxModelImportForm',
  13. 'OrganizationalModelImportForm',
  14. 'OwnerCSVMixin',
  15. 'PrimaryModelImportForm'
  16. )
  17. class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
  18. """
  19. Base form for creating NetBox objects from CSV data. Used for bulk importing.
  20. """
  21. tags = CSVModelMultipleChoiceField(
  22. label=_('Tags'),
  23. queryset=Tag.objects.all(),
  24. required=False,
  25. to_field_name='slug',
  26. help_text=_('Tag slugs separated by commas, encased with double quotes (e.g. "tag1,tag2,tag3")')
  27. )
  28. def _get_custom_fields(self, content_type):
  29. # Return only custom fields that are editable in the UI
  30. return [
  31. cf for cf in CustomField.objects.get_for_model(content_type.model_class())
  32. if cf.ui_editable == CustomFieldUIEditableChoices.YES
  33. ]
  34. def _get_form_field(self, customfield):
  35. return customfield.to_form_field(for_csv_import=True)
  36. def clean(self):
  37. """
  38. Cleans data in a form, ensuring proper handling of model fields with `null=True`.
  39. Overrides the `clean` method from the parent form to process and sanitize cleaned
  40. data for defined fields in the associated model.
  41. """
  42. super().clean()
  43. cleaned = self.cleaned_data
  44. model = getattr(self._meta, "model", None)
  45. if not model:
  46. return cleaned
  47. for f in model._meta.get_fields():
  48. # Only forward, DB-backed fields (skip M2M & reverse relations)
  49. if not isinstance(f, models.Field) or not f.concrete or f.many_to_many:
  50. continue
  51. if getattr(f, "null", False):
  52. name = f.name
  53. if name not in cleaned:
  54. continue
  55. val = cleaned[name]
  56. # Only coerce empty strings; leave other types alone
  57. if isinstance(val, str) and val.strip() == "":
  58. cleaned[name] = None
  59. return cleaned
  60. class OwnerCSVMixin(forms.Form):
  61. owner = CSVModelChoiceField(
  62. queryset=Owner.objects.all(),
  63. required=False,
  64. to_field_name='name',
  65. help_text=_("Name of the object's owner")
  66. )
  67. class PrimaryModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  68. """
  69. Bulk import form for models which inherit from PrimaryModel.
  70. """
  71. pass
  72. class OrganizationalModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  73. """
  74. Bulk import form for models which inherit from OrganizationalModel.
  75. """
  76. slug = SlugField()
  77. class NestedGroupModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  78. """
  79. Bulk import form for models which inherit from NestedGroupModel.
  80. """
  81. slug = SlugField()