netbox-plugin-dns 0.22.9__py3-none-any.whl → 1.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of netbox-plugin-dns might be problematic. Click here for more details.

Files changed (103) hide show
  1. netbox_dns/__init__.py +3 -14
  2. netbox_dns/api/nested_serializers.py +4 -60
  3. netbox_dns/api/serializers.py +8 -314
  4. netbox_dns/api/serializers_/__init__.py +0 -0
  5. netbox_dns/api/serializers_/contact.py +37 -0
  6. netbox_dns/api/serializers_/nameserver.py +39 -0
  7. netbox_dns/api/serializers_/record.py +90 -0
  8. netbox_dns/api/serializers_/registrar.py +31 -0
  9. netbox_dns/api/serializers_/view.py +29 -0
  10. netbox_dns/api/serializers_/zone.py +170 -0
  11. netbox_dns/api/views.py +13 -13
  12. netbox_dns/fields/network.py +1 -1
  13. netbox_dns/fields/rfc2317.py +1 -1
  14. netbox_dns/{filters → filtersets}/contact.py +2 -1
  15. netbox_dns/filtersets/nameserver.py +37 -0
  16. netbox_dns/{filters → filtersets}/record.py +51 -6
  17. netbox_dns/{filters → filtersets}/registrar.py +2 -1
  18. netbox_dns/{filters → filtersets}/view.py +2 -2
  19. netbox_dns/filtersets/zone.py +205 -0
  20. netbox_dns/forms/contact.py +61 -33
  21. netbox_dns/forms/nameserver.py +21 -8
  22. netbox_dns/forms/record.py +35 -36
  23. netbox_dns/forms/registrar.py +45 -15
  24. netbox_dns/forms/view.py +19 -8
  25. netbox_dns/forms/zone.py +112 -87
  26. netbox_dns/graphql/__init__.py +17 -27
  27. netbox_dns/graphql/filters.py +49 -0
  28. netbox_dns/graphql/schema.py +66 -7
  29. netbox_dns/graphql/types.py +146 -0
  30. netbox_dns/management/commands/cleanup_database.py +0 -4
  31. netbox_dns/management/commands/cleanup_rrset_ttl.py +3 -5
  32. netbox_dns/management/commands/setup_coupling.py +15 -15
  33. netbox_dns/management/commands/update_soa.py +1 -1
  34. netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -27
  35. netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +556 -0
  36. netbox_dns/migrations/{0013_add_nameserver_zone_record_description.py → 0002_contact_description_registrar_description.py} +4 -9
  37. netbox_dns/migrations/0020_netbox_3_4.py +1 -1
  38. netbox_dns/models/contact.py +6 -1
  39. netbox_dns/models/nameserver.py +3 -3
  40. netbox_dns/models/record.py +8 -15
  41. netbox_dns/models/registrar.py +5 -1
  42. netbox_dns/models/view.py +1 -1
  43. netbox_dns/models/zone.py +6 -9
  44. netbox_dns/navigation.py +1 -15
  45. netbox_dns/signals/ipam_coupling.py +3 -8
  46. netbox_dns/tables/contact.py +1 -0
  47. netbox_dns/tables/nameserver.py +0 -2
  48. netbox_dns/tables/registrar.py +1 -0
  49. netbox_dns/template_content.py +3 -7
  50. netbox_dns/templates/netbox_dns/contact.html +60 -56
  51. netbox_dns/templates/netbox_dns/nameserver.html +27 -27
  52. netbox_dns/templates/netbox_dns/record.html +92 -94
  53. netbox_dns/templates/netbox_dns/registrar.html +38 -36
  54. netbox_dns/templates/netbox_dns/view.html +21 -21
  55. netbox_dns/templates/netbox_dns/zone/base.html +5 -3
  56. netbox_dns/templates/netbox_dns/zone/child.html +3 -3
  57. netbox_dns/templates/netbox_dns/zone/managed_record.html +1 -1
  58. netbox_dns/templates/netbox_dns/zone/record.html +3 -3
  59. netbox_dns/templates/netbox_dns/zone/registration.html +26 -27
  60. netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +1 -1
  61. netbox_dns/templates/netbox_dns/zone.html +150 -149
  62. netbox_dns/utilities/__init__.py +1 -6
  63. netbox_dns/utilities/ipam_coupling.py +2 -7
  64. netbox_dns/validators/dns_name.py +4 -9
  65. netbox_dns/validators/rfc2317.py +2 -2
  66. netbox_dns/views/contact.py +4 -4
  67. netbox_dns/views/nameserver.py +5 -5
  68. netbox_dns/views/record.py +19 -12
  69. netbox_dns/views/registrar.py +4 -4
  70. netbox_dns/views/view.py +4 -4
  71. netbox_dns/views/zone.py +7 -8
  72. {netbox_plugin_dns-0.22.9.dist-info → netbox_plugin_dns-1.0b1.dist-info}/METADATA +5 -7
  73. netbox_plugin_dns-1.0b1.dist-info/RECORD +103 -0
  74. netbox_dns/filters/nameserver.py +0 -18
  75. netbox_dns/filters/zone.py +0 -112
  76. netbox_dns/graphql/contact.py +0 -19
  77. netbox_dns/graphql/nameserver.py +0 -19
  78. netbox_dns/graphql/record.py +0 -19
  79. netbox_dns/graphql/registrar.py +0 -19
  80. netbox_dns/graphql/view.py +0 -19
  81. netbox_dns/graphql/zone.py +0 -19
  82. netbox_dns/migrations/0001_initial.py +0 -115
  83. netbox_dns/migrations/0002_zone_default_ttl.py +0 -18
  84. netbox_dns/migrations/0003_soa_managed_records.py +0 -112
  85. netbox_dns/migrations/0004_create_ptr_for_a_aaaa_records.py +0 -80
  86. netbox_dns/migrations/0005_update_ns_records.py +0 -41
  87. netbox_dns/migrations/0006_zone_soa_serial_auto.py +0 -29
  88. netbox_dns/migrations/0007_alter_zone_soa_serial_auto.py +0 -17
  89. netbox_dns/migrations/0008_zone_status_names.py +0 -21
  90. netbox_dns/migrations/0009_netbox32.py +0 -71
  91. netbox_dns/migrations/0010_update_soa_records.py +0 -58
  92. netbox_dns/migrations/0011_add_view_model.py +0 -70
  93. netbox_dns/migrations/0012_adjust_zone_and_record.py +0 -17
  94. netbox_dns/migrations/0014_add_view_description.py +0 -16
  95. netbox_dns/migrations/0015_add_record_status.py +0 -17
  96. netbox_dns/migrations/0016_cleanup_ptr_records.py +0 -38
  97. netbox_dns/migrations/0017_alter_record_ttl.py +0 -17
  98. netbox_dns/migrations/0018_zone_arpa_network.py +0 -51
  99. netbox_dns/migrations/0019_update_ns_ttl.py +0 -19
  100. netbox_plugin_dns-0.22.9.dist-info/RECORD +0 -117
  101. /netbox_dns/{filters → filtersets}/__init__.py +0 -0
  102. {netbox_plugin_dns-0.22.9.dist-info → netbox_plugin_dns-1.0b1.dist-info}/LICENSE +0 -0
  103. {netbox_plugin_dns-0.22.9.dist-info → netbox_plugin_dns-1.0b1.dist-info}/WHEEL +0 -0
@@ -7,15 +7,38 @@ from netbox.forms import (
7
7
  NetBoxModelForm,
8
8
  )
9
9
  from utilities.forms.fields import TagFilterField
10
+ from utilities.forms.rendering import FieldSet
10
11
 
11
12
  from netbox_dns.models import Contact
12
13
 
13
14
 
14
15
  class ContactForm(NetBoxModelForm):
16
+ fieldsets = (
17
+ FieldSet(
18
+ "name",
19
+ "description",
20
+ "contact_id",
21
+ "organization",
22
+ "street",
23
+ "city",
24
+ "state_province",
25
+ "postal_code",
26
+ "country",
27
+ "phone",
28
+ "phone_ext",
29
+ "fax",
30
+ "fax_ext",
31
+ "email",
32
+ name="Contact",
33
+ ),
34
+ FieldSet("tags", name="Tags"),
35
+ )
36
+
15
37
  class Meta:
16
38
  model = Contact
17
39
  fields = (
18
40
  "name",
41
+ "description",
19
42
  "contact_id",
20
43
  "organization",
21
44
  "street",
@@ -34,25 +57,28 @@ class ContactForm(NetBoxModelForm):
34
57
 
35
58
  class ContactFilterForm(NetBoxModelFilterSetForm):
36
59
  model = Contact
60
+
37
61
  fieldsets = (
38
- (None, ("q", "name", "tags", "contact_id")),
39
- (
40
- "Address",
41
- (
42
- "organization",
43
- "street",
44
- "city",
45
- "state_province",
46
- "postal_code",
47
- "country",
48
- ),
62
+ FieldSet("q", "filter_id", "tag"),
63
+ FieldSet("name", "contact_id", "description", name="Attributes"),
64
+ FieldSet(
65
+ "organization",
66
+ "street",
67
+ "city",
68
+ "state_province",
69
+ "postal_code",
70
+ "country",
71
+ name="Address",
49
72
  ),
50
- ("Communication", ("phone", "phone_ext", "fax", "fax_ext", "email")),
73
+ FieldSet("phone", "phone_ext", "fax", "fax_ext", "email", name="Communication"),
51
74
  )
52
75
 
53
76
  name = forms.CharField(
54
77
  required=False,
55
78
  )
79
+ description = forms.CharField(
80
+ required=False,
81
+ )
56
82
  contact_id = forms.CharField(
57
83
  required=False,
58
84
  label="Contact ID",
@@ -94,7 +120,7 @@ class ContactFilterForm(NetBoxModelFilterSetForm):
94
120
  email = forms.CharField(
95
121
  required=False,
96
122
  )
97
- tags = TagFilterField(Contact)
123
+ tag = TagFilterField(Contact)
98
124
 
99
125
 
100
126
  class ContactImportForm(NetBoxModelImportForm):
@@ -102,6 +128,7 @@ class ContactImportForm(NetBoxModelImportForm):
102
128
  model = Contact
103
129
  fields = (
104
130
  "name",
131
+ "description",
105
132
  "contact_id",
106
133
  "organization",
107
134
  "street",
@@ -125,6 +152,10 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm):
125
152
  required=False,
126
153
  label="Name",
127
154
  )
155
+ description = forms.CharField(
156
+ required=False,
157
+ label="Description",
158
+ )
128
159
  organization = forms.CharField(
129
160
  required=False,
130
161
  label="Organization",
@@ -171,32 +202,29 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm):
171
202
  )
172
203
 
173
204
  fieldsets = (
174
- (None, ("name",)),
175
- (
176
- "Address",
177
- (
178
- "organization",
179
- "street",
180
- "city",
181
- "state_province",
182
- "postal_code",
183
- "country",
184
- ),
205
+ FieldSet("name", "description", name="Attributes"),
206
+ FieldSet(
207
+ "organization",
208
+ "street",
209
+ "city",
210
+ "state_province",
211
+ "postal_code",
212
+ "country",
213
+ name="Address",
185
214
  ),
186
- (
187
- "Communication",
188
- (
189
- "phone",
190
- "phone_ext",
191
- "fax",
192
- "fax_ext",
193
- "email",
194
- ),
215
+ FieldSet(
216
+ "phone",
217
+ "phone_ext",
218
+ "fax",
219
+ "fax_ext",
220
+ "email",
221
+ name="Communication",
195
222
  ),
196
223
  )
197
224
 
198
225
  nullable_fields = (
199
226
  "name",
227
+ "description",
200
228
  "organization",
201
229
  "street",
202
230
  "city",
@@ -12,6 +12,7 @@ from utilities.forms.fields import (
12
12
  CSVModelChoiceField,
13
13
  DynamicModelChoiceField,
14
14
  )
15
+ from utilities.forms.rendering import FieldSet
15
16
  from tenancy.models import Tenant
16
17
  from tenancy.forms import TenancyForm, TenancyFilterForm
17
18
 
@@ -28,8 +29,9 @@ class NameServerForm(TenancyForm, NetBoxModelForm):
28
29
  self.initial["name"] = name_to_unicode(initial_name)
29
30
 
30
31
  fieldsets = (
31
- ("Nameserver", ("name", "description", "tags")),
32
- ("Tenancy", ("tenant_group", "tenant")),
32
+ FieldSet("name", "description", name="Nameserver"),
33
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
34
+ FieldSet("tags", name="Tags"),
33
35
  )
34
36
 
35
37
  class Meta:
@@ -42,14 +44,16 @@ class NameServerFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
42
44
 
43
45
  name = forms.CharField(
44
46
  required=False,
45
- label="Name",
47
+ )
48
+ description = forms.CharField(
49
+ required=False,
46
50
  )
47
51
  tag = TagFilterField(NameServer)
48
52
 
49
53
  fieldsets = (
50
- (None, ("q", "filter_id", "tag")),
51
- ("Attributes", ("name",)),
52
- ("Tenant", ("tenant_group_id", "tenant_id")),
54
+ FieldSet("q", "filter_id", "tag"),
55
+ FieldSet("name", "description", name="Attributes"),
56
+ FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
53
57
  )
54
58
 
55
59
 
@@ -78,5 +82,14 @@ class NameServerBulkEditForm(NetBoxModelBulkEditForm):
78
82
  description = forms.CharField(max_length=200, required=False)
79
83
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
80
84
 
81
- class Meta:
82
- nullable_fields = ("description", "tenant")
85
+ fieldsets = (
86
+ FieldSet(
87
+ "name",
88
+ "description",
89
+ "tenant",
90
+ "tags",
91
+ name="Attributes",
92
+ ),
93
+ )
94
+
95
+ nullable_fields = ("description", "tenant")
@@ -16,6 +16,7 @@ from utilities.forms.fields import (
16
16
  )
17
17
  from utilities.forms.widgets import BulkEditNullBooleanSelect, APISelect
18
18
  from utilities.forms import add_blank_choice
19
+ from utilities.forms.rendering import FieldSet
19
20
  from tenancy.models import Tenant
20
21
  from tenancy.forms import TenancyForm, TenancyFilterForm
21
22
 
@@ -41,21 +42,19 @@ class RecordForm(TenancyForm, NetBoxModelForm):
41
42
  )
42
43
 
43
44
  fieldsets = (
44
- (
45
- "Record",
46
- (
47
- "name",
48
- "zone",
49
- "type",
50
- "value",
51
- "status",
52
- "ttl",
53
- "disable_ptr",
54
- "description",
55
- "tags",
56
- ),
45
+ FieldSet(
46
+ "name",
47
+ "zone",
48
+ "type",
49
+ "value",
50
+ "status",
51
+ "ttl",
52
+ "disable_ptr",
53
+ "description",
54
+ name="Record",
57
55
  ),
58
- ("Tenancy", ("tenant_group", "tenant")),
56
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
57
+ FieldSet("tags", name="Tags"),
59
58
  )
60
59
 
61
60
  class Meta:
@@ -78,12 +77,18 @@ class RecordForm(TenancyForm, NetBoxModelForm):
78
77
  class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
79
78
  model = Record
80
79
  fieldsets = (
81
- (None, ("q", "filter_id", "tag")),
82
- (
83
- "Attributes",
84
- ("view_id", "zone_id", "name", "fqdn", "type", "value", "status"),
80
+ FieldSet("q", "filter_id", "tag"),
81
+ FieldSet(
82
+ "zone_id",
83
+ "name",
84
+ "fqdn",
85
+ "type",
86
+ "value",
87
+ "status",
88
+ "description",
89
+ name="Attributes",
85
90
  ),
86
- ("Tenant", ("tenant_group_id", "tenant_id")),
91
+ FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
87
92
  )
88
93
 
89
94
  type = forms.MultipleChoiceField(
@@ -92,7 +97,6 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
92
97
  )
93
98
  name = forms.CharField(
94
99
  required=False,
95
- label="Name",
96
100
  )
97
101
  fqdn = forms.CharField(
98
102
  required=False,
@@ -100,7 +104,6 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
100
104
  )
101
105
  value = forms.CharField(
102
106
  required=False,
103
- label="Value",
104
107
  )
105
108
  status = forms.MultipleChoiceField(
106
109
  choices=RecordStatusChoices,
@@ -111,10 +114,8 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
111
114
  required=False,
112
115
  label="Zone",
113
116
  )
114
- view_id = DynamicModelMultipleChoiceField(
115
- queryset=View.objects.all(),
117
+ description = forms.CharField(
116
118
  required=False,
117
- label="View",
118
119
  )
119
120
  tag = TagFilterField(Record)
120
121
 
@@ -231,18 +232,16 @@ class RecordBulkEditForm(NetBoxModelBulkEditForm):
231
232
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
232
233
 
233
234
  fieldsets = (
234
- (
235
- None,
236
- (
237
- "zone",
238
- "type",
239
- "value",
240
- "status",
241
- "ttl",
242
- "disable_ptr",
243
- "description",
244
- "tenant",
245
- ),
235
+ FieldSet(
236
+ "zone",
237
+ "type",
238
+ "value",
239
+ "status",
240
+ "ttl",
241
+ "disable_ptr",
242
+ "description",
243
+ name="Attributes",
246
244
  ),
245
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
247
246
  )
248
247
  nullable_fields = ("description", "ttl", "tenant")
@@ -7,6 +7,7 @@ from netbox.forms import (
7
7
  NetBoxModelForm,
8
8
  )
9
9
  from utilities.forms.fields import TagFilterField
10
+ from utilities.forms.rendering import FieldSet
10
11
 
11
12
  from netbox_dns.models import Registrar
12
13
 
@@ -14,9 +15,24 @@ from netbox_dns.models import Registrar
14
15
  class RegistrarForm(NetBoxModelForm):
15
16
  class Meta:
16
17
  model = Registrar
18
+ fieldsets = (
19
+ FieldSet(
20
+ "name",
21
+ "iana_id",
22
+ "description",
23
+ "address",
24
+ "referral_url",
25
+ "whois_server",
26
+ "abuse_email",
27
+ "abuse_phone",
28
+ name="Registrar",
29
+ ),
30
+ FieldSet("tags", name="Tags"),
31
+ )
17
32
  fields = (
18
33
  "name",
19
34
  "iana_id",
35
+ "description",
20
36
  "address",
21
37
  "referral_url",
22
38
  "whois_server",
@@ -29,10 +45,15 @@ class RegistrarForm(NetBoxModelForm):
29
45
  class RegistrarFilterForm(NetBoxModelFilterSetForm):
30
46
  model = Registrar
31
47
  fieldsets = (
32
- (None, ("q", "name", "iana_id", "tags")),
33
- (
34
- "Contact",
35
- ("address", "referral_url", "whois_server", "abuse_email", "abuse_phone"),
48
+ FieldSet("q", "filter_id", "tag"),
49
+ FieldSet("name", "iana_id", "description", name="Attributes"),
50
+ FieldSet(
51
+ "address",
52
+ "referral_url",
53
+ "whois_server",
54
+ "abuse_email",
55
+ "abuse_phone",
56
+ name="Contact",
36
57
  ),
37
58
  )
38
59
 
@@ -42,6 +63,9 @@ class RegistrarFilterForm(NetBoxModelFilterSetForm):
42
63
  address = forms.CharField(
43
64
  required=False,
44
65
  )
66
+ description = forms.CharField(
67
+ required=False,
68
+ )
45
69
  iana_id = forms.IntegerField(
46
70
  required=False,
47
71
  label="IANA ID",
@@ -62,7 +86,7 @@ class RegistrarFilterForm(NetBoxModelFilterSetForm):
62
86
  required=False,
63
87
  label="Abuse Phone",
64
88
  )
65
- tags = TagFilterField(Registrar)
89
+ tag = TagFilterField(Registrar)
66
90
 
67
91
 
68
92
  class RegistrarImportForm(NetBoxModelImportForm):
@@ -71,6 +95,7 @@ class RegistrarImportForm(NetBoxModelImportForm):
71
95
  fields = (
72
96
  "name",
73
97
  "iana_id",
98
+ "description",
74
99
  "address",
75
100
  "referral_url",
76
101
  "whois_server",
@@ -87,8 +112,13 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
87
112
  required=False,
88
113
  label="IANA ID",
89
114
  )
115
+ description = forms.CharField(
116
+ required=False,
117
+ label="Description",
118
+ )
90
119
  address = forms.CharField(
91
120
  required=False,
121
+ label="Address",
92
122
  )
93
123
  referral_url = forms.CharField(
94
124
  required=False,
@@ -108,21 +138,21 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
108
138
  )
109
139
 
110
140
  fieldsets = (
111
- (
112
- None,
113
- (
114
- "iana_id",
115
- "address",
116
- "referral_url",
117
- "whois_server",
118
- "abuse_email",
119
- "abuse_phone",
120
- ),
141
+ FieldSet(
142
+ "iana_id",
143
+ "description",
144
+ "address",
145
+ "referral_url",
146
+ "whois_server",
147
+ "abuse_email",
148
+ "abuse_phone",
149
+ name="Attributes",
121
150
  ),
122
151
  )
123
152
 
124
153
  nullable_fields = (
125
154
  "iana_id",
155
+ "description",
126
156
  "address",
127
157
  "referral_url",
128
158
  "whois_server",
netbox_dns/forms/view.py CHANGED
@@ -11,6 +11,7 @@ from utilities.forms.fields import (
11
11
  CSVModelChoiceField,
12
12
  DynamicModelChoiceField,
13
13
  )
14
+ from utilities.forms.rendering import FieldSet
14
15
  from tenancy.models import Tenant
15
16
  from tenancy.forms import TenancyForm, TenancyFilterForm
16
17
 
@@ -19,8 +20,8 @@ from netbox_dns.models import View
19
20
 
20
21
  class ViewForm(TenancyForm, NetBoxModelForm):
21
22
  fieldsets = (
22
- ("View", ("name", "description", "tags")),
23
- ("Tenancy", ("tenant_group", "tenant")),
23
+ FieldSet("name", "description", "tags", name="View"),
24
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
24
25
  )
25
26
 
26
27
  class Meta:
@@ -31,14 +32,16 @@ class ViewForm(TenancyForm, NetBoxModelForm):
31
32
  class ViewFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
32
33
  model = View
33
34
  fieldsets = (
34
- (None, ("q", "filter_id", "tag")),
35
- ("Attributes", ("name",)),
36
- ("Tenant", ("tenant_group_id", "tenant_id")),
35
+ FieldSet("q", "filter_id", "tag"),
36
+ FieldSet("name", "description", name="Attributes"),
37
+ FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
37
38
  )
38
39
 
39
40
  name = forms.CharField(
40
41
  required=False,
41
- label="Name",
42
+ )
43
+ description = forms.CharField(
44
+ required=False,
42
45
  )
43
46
  tag = TagFilterField(View)
44
47
 
@@ -62,5 +65,13 @@ class ViewBulkEditForm(NetBoxModelBulkEditForm):
62
65
  description = forms.CharField(max_length=200, required=False)
63
66
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
64
67
 
65
- class Meta:
66
- nullable_fields = ("description", "tenant")
68
+ fieldsets = (
69
+ FieldSet(
70
+ "name",
71
+ "description",
72
+ name="Attributes",
73
+ ),
74
+ FieldSet("tenant", name="Tenancy"),
75
+ )
76
+
77
+ nullable_fields = ("description", "tenant")