netboxlabs-netbox-custom-objects 0.1.0__tar.gz

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.
Files changed (40) hide show
  1. netboxlabs_netbox_custom_objects-0.1.0/LICENSE.md +85 -0
  2. netboxlabs_netbox_custom_objects-0.1.0/PKG-INFO +61 -0
  3. netboxlabs_netbox_custom_objects-0.1.0/README.md +30 -0
  4. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/__init__.py +42 -0
  5. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/api/__init__.py +0 -0
  6. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/api/serializers.py +244 -0
  7. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/api/urls.py +83 -0
  8. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/api/views.py +49 -0
  9. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/choices.py +20 -0
  10. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/constants.py +8 -0
  11. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/field_types.py +796 -0
  12. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/fields.py +86 -0
  13. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/filtersets.py +21 -0
  14. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/forms.py +144 -0
  15. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/migrations/0001_initial.py +199 -0
  16. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/migrations/__init__.py +0 -0
  17. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/models.py +1056 -0
  18. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/navigation.py +70 -0
  19. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/tables.py +183 -0
  20. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/template_content.py +82 -0
  21. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/buttons/custom_objects_delete.html +4 -0
  22. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/custom_object_list.html +30 -0
  23. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/customobject.html +134 -0
  24. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/customobject_edit.html +12 -0
  25. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/customobjecttype.html +157 -0
  26. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/field_delete.html +30 -0
  27. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templates/netbox_custom_objects/htmx/delete_form.html +30 -0
  28. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templatetags/__init__.py +0 -0
  29. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templatetags/custom_object_buttons.py +246 -0
  30. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/templatetags/custom_object_utils.py +49 -0
  31. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/urls.py +63 -0
  32. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/utilities.py +85 -0
  33. netboxlabs_netbox_custom_objects-0.1.0/netbox_custom_objects/views.py +453 -0
  34. netboxlabs_netbox_custom_objects-0.1.0/netboxlabs_netbox_custom_objects.egg-info/PKG-INFO +61 -0
  35. netboxlabs_netbox_custom_objects-0.1.0/netboxlabs_netbox_custom_objects.egg-info/SOURCES.txt +38 -0
  36. netboxlabs_netbox_custom_objects-0.1.0/netboxlabs_netbox_custom_objects.egg-info/dependency_links.txt +1 -0
  37. netboxlabs_netbox_custom_objects-0.1.0/netboxlabs_netbox_custom_objects.egg-info/requires.txt +12 -0
  38. netboxlabs_netbox_custom_objects-0.1.0/netboxlabs_netbox_custom_objects.egg-info/top_level.txt +1 -0
  39. netboxlabs_netbox_custom_objects-0.1.0/pyproject.toml +49 -0
  40. netboxlabs_netbox_custom_objects-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,85 @@
1
+ # NetBox Limited Use License 1.0
2
+
3
+ ## Acceptance
4
+
5
+ In order to get any license under these terms, you must agree to them as
6
+ both strict obligations and conditions to all your licenses.
7
+
8
+ ## Copyright License
9
+
10
+ NetBox Labs grants you a copyright license to use and modify the software
11
+ only as part of a NetBox installation obtained from NetBox Labs or a NetBox
12
+ distributor authorized by NetBox Labs, and only for your own internal use.
13
+
14
+ For clarity, this license grants no rights to distribute the software or
15
+ make it available to others as part of a commercial offering.
16
+
17
+ ## Patent License
18
+
19
+ NetBox Labs grants you a patent license for the software that covers patent
20
+ claims the licensor can license, or becomes able to license, that you would
21
+ infringe by using the software, as allowed in the copyright license above.
22
+
23
+ ## Patent Defense
24
+
25
+ If you make any written claim that the software infringes or contributes to
26
+ infringement of any patent, your patent license for the software granted
27
+ under these terms ends immediately. If your company makes such a claim,
28
+ your patent license ends immediately for work on behalf of your company.
29
+ Competitive Restrictions
30
+
31
+ This license does not grant you the right to use the software:
32
+
33
+ - To provide a managed service or software products that includes, integrates
34
+ with, or extends NetBox in a way that competes with any product or service
35
+ of NetBox Labs.
36
+
37
+ - To assist or enable a third party in offering a service or product that
38
+ competes with any product or service of NetBox Labs.
39
+
40
+ ## No Other Rights
41
+
42
+ These terms do not allow you to sublicense or transfer any of your licenses
43
+ to anyone else, or prevent NetBox Labs from granting licenses to anyone else.
44
+ These terms do not imply any other licenses.
45
+
46
+ ## Violations
47
+
48
+ The first time you are notified in writing that you have violated any of
49
+ these terms, or done anything with the software not covered by your licenses,
50
+ your licenses can nonetheless continue if you come into full compliance with
51
+ these terms, and take practical steps to correct past violations, within 30
52
+ days of receiving notice. Otherwise, all your licenses end immediately.
53
+
54
+ ## No Liability
55
+
56
+ As far as the law allows, the software comes as is, without any warranty or
57
+ condition, and NetBox Labs will not be liable to you for any damages arising
58
+ out of these terms or the use or nature of the software, under any kind of
59
+ legal claim.
60
+
61
+ If this disclaimer is unenforceable under applicable law, this license is void.
62
+
63
+ ## Definitions
64
+
65
+ **NetBox Labs** is NetBox Labs, Inc.
66
+
67
+ **NetBox** is the community edition of NetBox found at
68
+ <https://github.com/netbox-community/netbox> or any derivative thereof.
69
+
70
+ The **software** is the software NetBox Labs makes available under these terms.
71
+
72
+ **You** refers to the individual or entity agreeing to these terms.
73
+
74
+ **Your company** is any legal entity, sole proprietorship, or other kind of
75
+ organization that you work for, plus all organizations that have control over,
76
+ are under the control of, or are under common control with that organization.
77
+
78
+ **Control** means ownership of substantially all the assets of an entity,
79
+ or the power to direct its management and policies by vote, contract, or
80
+ otherwise. Control can be direct or indirect.
81
+
82
+ **Your licenses** are all the licenses granted to you for the software under
83
+ these terms.
84
+
85
+ **Use** means anything you do with the software requiring one of your licenses.
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.4
2
+ Name: netboxlabs-netbox-custom-objects
3
+ Version: 0.1.0
4
+ Summary: A plugin to manage custom objects in NetBox
5
+ Author-email: NetBox Labs <support@netboxlabs.com>
6
+ Maintainer-email: NetBox Labs <support@netboxlabs.com>
7
+ License: PolyForm Shield License 1.0.0
8
+ Project-URL: Homepage, https://netboxlabs.com/
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Build Tools
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE.md
20
+ Requires-Dist: Django
21
+ Provides-Extra: dev
22
+ Requires-Dist: check-manifest; extra == "dev"
23
+ Requires-Dist: mkdocs; extra == "dev"
24
+ Requires-Dist: mkdocs-material; extra == "dev"
25
+ Requires-Dist: pycodestyle; extra == "dev"
26
+ Provides-Extra: test
27
+ Requires-Dist: coverage; extra == "test"
28
+ Requires-Dist: pytest; extra == "test"
29
+ Requires-Dist: pytest-cov; extra == "test"
30
+ Dynamic: license-file
31
+
32
+ # NetBox Custom Objects
33
+
34
+ This [NetBox](https://netboxlabs.com/products/netbox/) plugin introduces the ability to create new object types in NetBox so that users can add models to suit their own needs. NetBox users have been able to extend the NetBox data model for some time using both Tags & Custom Fields and Plugins. Tags and Custom Fields are easy to use, but they have limitations when used at scale, and Plugins are very powerful but require Python/Django knowledge, and ongoing maintenance. Custom Objects provides users with a no-code "sweet spot" for data model extensibility, providing a lot of the power of NetBox plugins, but with the ease of use of Tags and Custom Fields.
35
+
36
+ You can find further documentation [here](docs/index.md).
37
+
38
+ ## Requirements
39
+
40
+ * NetBox v4.2 or later
41
+
42
+ ## Installation
43
+
44
+ 1. Add `netboxlabs_netbox_custom_objects` to `PLUGINS` in `configuration.py`.
45
+
46
+ ```python
47
+ PLUGINS = [
48
+ # ...
49
+ 'netboxlabs_netbox_custom_objects',
50
+ ]
51
+ ```
52
+
53
+ 2. Run NetBox migrations:
54
+
55
+ ```
56
+ $ ./manage.py migrate
57
+ ```
58
+
59
+ ## Known Limitations
60
+
61
+ The Public Preview of NetBox Custom Objects is under active development as we proceed towards the General Availability release around NetBox 4.4. The best place to look for the latest list of known limitations is the [issues](https://github.com/netboxlabs/netbox-custom-objects/issues) list on the GitHub repository. These include features like Tags, Import/Export, Bulk Edit, Text Search and Branching.
@@ -0,0 +1,30 @@
1
+ # NetBox Custom Objects
2
+
3
+ This [NetBox](https://netboxlabs.com/products/netbox/) plugin introduces the ability to create new object types in NetBox so that users can add models to suit their own needs. NetBox users have been able to extend the NetBox data model for some time using both Tags & Custom Fields and Plugins. Tags and Custom Fields are easy to use, but they have limitations when used at scale, and Plugins are very powerful but require Python/Django knowledge, and ongoing maintenance. Custom Objects provides users with a no-code "sweet spot" for data model extensibility, providing a lot of the power of NetBox plugins, but with the ease of use of Tags and Custom Fields.
4
+
5
+ You can find further documentation [here](docs/index.md).
6
+
7
+ ## Requirements
8
+
9
+ * NetBox v4.2 or later
10
+
11
+ ## Installation
12
+
13
+ 1. Add `netboxlabs_netbox_custom_objects` to `PLUGINS` in `configuration.py`.
14
+
15
+ ```python
16
+ PLUGINS = [
17
+ # ...
18
+ 'netboxlabs_netbox_custom_objects',
19
+ ]
20
+ ```
21
+
22
+ 2. Run NetBox migrations:
23
+
24
+ ```
25
+ $ ./manage.py migrate
26
+ ```
27
+
28
+ ## Known Limitations
29
+
30
+ The Public Preview of NetBox Custom Objects is under active development as we proceed towards the General Availability release around NetBox 4.4. The best place to look for the latest list of known limitations is the [issues](https://github.com/netboxlabs/netbox-custom-objects/issues) list on the GitHub repository. These include features like Tags, Import/Export, Bulk Edit, Text Search and Branching.
@@ -0,0 +1,42 @@
1
+ from netbox.plugins import PluginConfig
2
+
3
+
4
+ # Plugin Configuration
5
+ class CustomObjectsPluginConfig(PluginConfig):
6
+ name = "netbox_custom_objects"
7
+ verbose_name = "Custom Objects"
8
+ description = "A plugin to manage custom objects in NetBox"
9
+ version = "0.1.0"
10
+ base_url = "custom-objects"
11
+ min_version = "4.2.0"
12
+ default_settings = {}
13
+ required_settings = []
14
+ template_extensions = "template_content.template_extensions"
15
+
16
+ # def get_model(self, model_name, require_ready=True):
17
+ # if require_ready:
18
+ # self.apps.check_models_ready()
19
+ # else:
20
+ # self.apps.check_apps_ready()
21
+ #
22
+ # if model_name.lower() in self.models:
23
+ # return self.models[model_name.lower()]
24
+ #
25
+ # from .models import CustomObjectType
26
+ # if "table" not in model_name.lower() or "model" not in model_name.lower():
27
+ # raise LookupError(
28
+ # "App '%s' doesn't have a '%s' model." % (self.label, model_name)
29
+ # )
30
+ #
31
+ # custom_object_type_id = int(model_name.replace("table", "").replace("model", ""))
32
+ #
33
+ # try:
34
+ # obj = CustomObjectType.objects.get(pk=custom_object_type_id)
35
+ # except CustomObjectType.DoesNotExist:
36
+ # raise LookupError(
37
+ # "App '%s' doesn't have a '%s' model." % (self.label, model_name)
38
+ # )
39
+ # return obj.get_model()
40
+
41
+
42
+ config = CustomObjectsPluginConfig
@@ -0,0 +1,244 @@
1
+ from django.contrib.contenttypes.models import ContentType
2
+ from extras.choices import CustomFieldTypeChoices
3
+ from netbox.api.serializers import NetBoxModelSerializer
4
+ from rest_framework import serializers
5
+ from rest_framework.exceptions import ValidationError
6
+ from rest_framework.reverse import reverse
7
+
8
+ from netbox_custom_objects import field_types
9
+ from netbox_custom_objects.models import CustomObject, CustomObjectType, CustomObjectTypeField
10
+
11
+ __all__ = (
12
+ "CustomObjectTypeSerializer",
13
+ "CustomObjectSerializer",
14
+ )
15
+
16
+
17
+ class ContentTypeSerializer(NetBoxModelSerializer):
18
+ class Meta:
19
+ model = ContentType
20
+ fields = (
21
+ "id",
22
+ "app_label",
23
+ "model",
24
+ )
25
+
26
+
27
+ class CustomObjectTypeFieldSerializer(NetBoxModelSerializer):
28
+ url = serializers.HyperlinkedIdentityField(
29
+ view_name="plugins-api:netbox_custom_objects-api:customobjecttypefield-detail"
30
+ )
31
+ app_label = serializers.CharField(required=False)
32
+ model = serializers.CharField(required=False)
33
+
34
+ class Meta:
35
+ model = CustomObjectTypeField
36
+ fields = (
37
+ # 'id', 'url', 'name', 'label', 'custom_object_type', 'field_type', 'content_type', 'many', 'options',
38
+ "id",
39
+ "name",
40
+ "label",
41
+ "custom_object_type",
42
+ "type",
43
+ "primary",
44
+ "default",
45
+ "choice_set",
46
+ "validation_regex",
47
+ "validation_minimum",
48
+ "validation_maximum",
49
+ "related_object_type",
50
+ "app_label",
51
+ "model",
52
+ )
53
+
54
+ def validate(self, attrs):
55
+ app_label = attrs.pop("app_label", None)
56
+ model = attrs.pop("model", None)
57
+ if attrs["type"] in [
58
+ CustomFieldTypeChoices.TYPE_OBJECT,
59
+ CustomFieldTypeChoices.TYPE_MULTIOBJECT,
60
+ ]:
61
+ try:
62
+ attrs["related_object_type"] = ContentType.objects.get(
63
+ app_label=app_label, model=model
64
+ )
65
+ except ContentType.DoesNotExist:
66
+ raise ValidationError(
67
+ "Must provide valid app_label and model for object field type."
68
+ )
69
+ if attrs["type"] in [
70
+ CustomFieldTypeChoices.TYPE_SELECT,
71
+ CustomFieldTypeChoices.TYPE_MULTISELECT,
72
+ ]:
73
+ if not attrs.get("choice_set", None):
74
+ raise ValidationError(
75
+ "Must provide choice_set with valid PK for select field type."
76
+ )
77
+ return super().validate(attrs)
78
+
79
+ def create(self, validated_data):
80
+ """
81
+ Record the user who created the Custom Object as its owner.
82
+ """
83
+ return super().create(validated_data)
84
+
85
+ def get_related_object_type(self, obj):
86
+ if obj.related_object_type:
87
+ return dict(
88
+ id=obj.related_object_type.id,
89
+ app_label=obj.related_object_type.app_label,
90
+ model=obj.related_object_type.model,
91
+ )
92
+
93
+
94
+ class CustomObjectTypeSerializer(NetBoxModelSerializer):
95
+ url = serializers.HyperlinkedIdentityField(
96
+ view_name="plugins-api:netbox_custom_objects-api:customobjecttype-detail"
97
+ )
98
+ fields = CustomObjectTypeFieldSerializer(
99
+ nested=True,
100
+ read_only=True,
101
+ many=True,
102
+ )
103
+
104
+ class Meta:
105
+ model = CustomObjectType
106
+ fields = [
107
+ "id",
108
+ "url",
109
+ "name",
110
+ "description",
111
+ "tags",
112
+ "created",
113
+ "last_updated",
114
+ "fields",
115
+ ]
116
+ brief_fields = ("id", "url", "name", "description")
117
+
118
+ def create(self, validated_data):
119
+ return super().create(validated_data)
120
+
121
+
122
+ # TODO: Remove or reduce to a stub (not needed as all custom object serializers are generated via get_serializer_class)
123
+ class CustomObjectSerializer(NetBoxModelSerializer):
124
+ relation_fields = None
125
+
126
+ url = serializers.SerializerMethodField()
127
+ field_data = serializers.SerializerMethodField()
128
+ custom_object_type = CustomObjectTypeSerializer(nested=True)
129
+
130
+ class Meta:
131
+ model = CustomObject
132
+ fields = [
133
+ "id",
134
+ "url",
135
+ "name",
136
+ "display",
137
+ "custom_object_type",
138
+ "tags",
139
+ "created",
140
+ "last_updated",
141
+ "data",
142
+ "field_data",
143
+ ]
144
+ brief_fields = (
145
+ "id",
146
+ "url",
147
+ "name",
148
+ "custom_object_type",
149
+ )
150
+
151
+ def get_display(self, obj):
152
+ return f"{obj.custom_object_type}: {obj.name}"
153
+
154
+ def validate(self, attrs):
155
+ return super().validate(attrs)
156
+
157
+ def update_relation_fields(self, instance):
158
+ # TODO: Implement this
159
+ pass
160
+
161
+ def create(self, validated_data):
162
+ model = validated_data["custom_object_type"].get_model()
163
+ instance = model.objects.create(**validated_data)
164
+
165
+ return instance
166
+
167
+ def update(self, instance, validated_data):
168
+ instance = super().update(instance, validated_data)
169
+ # self.update_relation_fields(instance)
170
+ return instance
171
+
172
+ def get_url(self, obj):
173
+ """
174
+ Given an object, return the URL that hyperlinks to the object.
175
+
176
+ May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
177
+ attributes are not configured to correctly match the URL conf.
178
+ """
179
+ # Unsaved objects will not yet have a valid URL.
180
+ if hasattr(obj, "pk") and obj.pk in (None, ""):
181
+ return None
182
+
183
+ view_name = "plugins-api:netbox_custom_objects-api:customobject-detail"
184
+ lookup_value = getattr(obj, "pk")
185
+ kwargs = {
186
+ "pk": lookup_value,
187
+ "custom_object_type": obj.custom_object_type.name.lower(),
188
+ }
189
+ request = self.context["request"]
190
+ format = self.context.get("format")
191
+ return reverse(view_name, kwargs=kwargs, request=request, format=format)
192
+
193
+ def get_field_data(self, obj):
194
+ result = {}
195
+ return result
196
+
197
+
198
+ def get_serializer_class(model):
199
+ model_fields = model.custom_object_type.fields.all()
200
+ meta = type(
201
+ "Meta",
202
+ (),
203
+ {
204
+ "model": model,
205
+ "fields": "__all__",
206
+ },
207
+ )
208
+
209
+ def get_url(self, obj):
210
+ # Unsaved objects will not yet have a valid URL.
211
+ if hasattr(obj, "pk") and obj.pk in (None, ""):
212
+ return None
213
+
214
+ view_name = "plugins-api:netbox_custom_objects-api:customobject-detail"
215
+ lookup_value = getattr(obj, "pk")
216
+ kwargs = {
217
+ "pk": lookup_value,
218
+ "custom_object_type": obj.custom_object_type.name.lower(),
219
+ }
220
+ request = self.context["request"]
221
+ format = self.context.get("format")
222
+ return reverse(view_name, kwargs=kwargs, request=request, format=format)
223
+
224
+ attrs = {
225
+ "Meta": meta,
226
+ "__module__": "database.serializers",
227
+ "url": serializers.SerializerMethodField(),
228
+ "get_url": get_url,
229
+ }
230
+
231
+ for field in model_fields:
232
+ field_type = field_types.FIELD_TYPE_CLASS[field.type]()
233
+ try:
234
+ attrs[field.name] = field_type.get_serializer_field(field)
235
+ except NotImplementedError:
236
+ print(f"serializer: {field.name} field is not implemented; using a default serializer field")
237
+
238
+ serializer = type(
239
+ f"{model._meta.object_name}Serializer",
240
+ (serializers.ModelSerializer,),
241
+ attrs,
242
+ )
243
+
244
+ return serializer
@@ -0,0 +1,83 @@
1
+ from copy import deepcopy
2
+ from django.urls import include, path, NoReverseMatch
3
+ from rest_framework.response import Response
4
+ from rest_framework.reverse import reverse
5
+ from rest_framework.views import APIView
6
+ from netbox.api.routers import NetBoxRouter
7
+ from netbox_custom_objects.models import CustomObjectType
8
+
9
+ from . import views
10
+
11
+ custom_object_list = views.CustomObjectViewSet.as_view(
12
+ {"get": "list", "post": "create"}
13
+ )
14
+ custom_object_detail = views.CustomObjectViewSet.as_view(
15
+ {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
16
+ )
17
+
18
+
19
+ class CustomObjectsAPIRootView(APIView):
20
+ """
21
+ This is the root of the NetBox Custom Objects plugin API. Custom Object Types defined at application startup
22
+ are listed by lowercased name; e.g. `/api/plugins/custom-objects/cat/`.
23
+ """
24
+ def get_view_name(self):
25
+ return "Custom Objects API Root"
26
+
27
+ _ignore_model_permissions = True
28
+ schema = None # exclude from schema
29
+ api_root_dict = None
30
+
31
+ # This logic is copied from stock DRF APIRootView
32
+ def get(self, request, *args, **kwargs):
33
+ # Return a plain {"name": "hyperlink"} response.
34
+ ret = {}
35
+ namespace = request.resolver_match.namespace
36
+ for key, url_name in self.api_root_dict.items():
37
+ if namespace:
38
+ url_name = namespace + ':' + url_name
39
+ try:
40
+ ret[key] = reverse(
41
+ url_name,
42
+ args=args,
43
+ kwargs=kwargs,
44
+ request=request,
45
+ format=kwargs.get('format')
46
+ )
47
+ except NoReverseMatch:
48
+ # Don't bail out if eg. no list routes exist, only detail routes.
49
+ continue
50
+
51
+ # Extra logic to populate roots for custom object type lists
52
+ for custom_object_type in CustomObjectType.objects.all():
53
+ local_kwargs = deepcopy(kwargs)
54
+ cot_name = custom_object_type.name.lower()
55
+ url_name = 'customobject-list'
56
+ local_kwargs['custom_object_type'] = cot_name
57
+ if namespace:
58
+ url_name = namespace + ':' + url_name
59
+ ret[cot_name] = reverse(
60
+ url_name,
61
+ args=args,
62
+ kwargs=local_kwargs,
63
+ request=request,
64
+ format=local_kwargs.get('format')
65
+ )
66
+
67
+ return Response(ret)
68
+
69
+
70
+ router = NetBoxRouter()
71
+ router.APIRootView = CustomObjectsAPIRootView
72
+ router.register("custom-object-types", views.CustomObjectTypeViewSet)
73
+ router.register("custom-object-type-fields", views.CustomObjectTypeFieldViewSet)
74
+
75
+ urlpatterns = [
76
+ path("", include(router.urls)),
77
+ path("<str:custom_object_type>/", custom_object_list, name="customobject-list"),
78
+ path(
79
+ "<str:custom_object_type>/<int:pk>/",
80
+ custom_object_detail,
81
+ name="customobject-detail",
82
+ ),
83
+ ]
@@ -0,0 +1,49 @@
1
+ from django.http import Http404
2
+ from rest_framework.routers import APIRootView
3
+ from rest_framework.viewsets import ModelViewSet
4
+
5
+ from netbox_custom_objects.models import CustomObject, CustomObjectType, CustomObjectTypeField
6
+
7
+ from . import serializers
8
+
9
+
10
+ class RootView(APIRootView):
11
+ def get_view_name(self):
12
+ return "CustomObjects"
13
+
14
+
15
+ class CustomObjectTypeViewSet(ModelViewSet):
16
+ queryset = CustomObjectType.objects.all()
17
+ serializer_class = serializers.CustomObjectTypeSerializer
18
+
19
+
20
+ class CustomObjectViewSet(ModelViewSet):
21
+ queryset = CustomObject.objects.all()
22
+ serializer_class = serializers.CustomObjectSerializer
23
+ model = None
24
+
25
+ def get_view_name(self):
26
+ if self.model:
27
+ return self.model.custom_object_type.name
28
+ return super().get_view_name()
29
+
30
+ def get_serializer_class(self):
31
+ return serializers.get_serializer_class(self.model)
32
+
33
+ def get_queryset(self):
34
+ try:
35
+ custom_object_type = CustomObjectType.objects.get(
36
+ name__iexact=self.kwargs["custom_object_type"]
37
+ )
38
+ except CustomObjectType.DoesNotExist:
39
+ raise Http404
40
+ self.model = custom_object_type.get_model()
41
+ return self.model.objects.all()
42
+
43
+ def list(self, request, *args, **kwargs):
44
+ return super().list(request, *args, **kwargs)
45
+
46
+
47
+ class CustomObjectTypeFieldViewSet(ModelViewSet):
48
+ queryset = CustomObjectTypeField.objects.all()
49
+ serializer_class = serializers.CustomObjectTypeFieldSerializer
@@ -0,0 +1,20 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from utilities.choices import ChoiceSet
3
+
4
+
5
+ class MappingFieldTypeChoices(ChoiceSet):
6
+ CHAR = "char"
7
+ INTEGER = "integer"
8
+ BOOLEAN = "boolean"
9
+ DATE = "date"
10
+ DATETIME = "datetime"
11
+ OBJECT = "object"
12
+
13
+ CHOICES = (
14
+ (CHAR, _("String"), "cyan"),
15
+ (INTEGER, _("Integer"), "orange"),
16
+ (BOOLEAN, _("Boolean"), "green"),
17
+ (DATE, _("Date"), "red"),
18
+ (DATETIME, _("DateTime"), "blue"),
19
+ (OBJECT, _("Object"), "orange"),
20
+ )
@@ -0,0 +1,8 @@
1
+ # Models which do not support change logging, but whose database tables
2
+ # must be replicated for each branch to ensure proper functionality
3
+ INCLUDE_MODELS = (
4
+ "dcim.cablepath",
5
+ "extras.cachedvalue",
6
+ )
7
+
8
+ APP_LABEL = "netbox_custom_objects"