From 6d777a20ad7f2daa48e390bfc6b2e48dbbcd84fe Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Dec 2025 16:40:35 -0500 Subject: [PATCH 1/5] Initial work on #13182 --- docs/plugins/development/filtersets.md | 8 ++++++++ docs/plugins/development/models.md | 18 ++++++++++++++++++ docs/plugins/development/rest-api.md | 8 ++++++++ docs/plugins/development/tables.md | 8 ++++++++ 4 files changed, 42 insertions(+) diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index cc847660d..cfaf9316b 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -32,6 +32,14 @@ class MyFilterSet(NetBoxModelFilterSet): fields = ('some', 'other', 'fields') ``` +In addition to the base NetBoxModelFilterSet class, the following filterset classes are also available for applicable models. + +| Model Class | FilterSet Class | +|-----------------------|--------------------------------------------------| +| `PrimaryModel` | `netbox.filtersets.PrimaryModelFilterSet` | +| `OrganizationalModel` | `netbox.filtersets.OrganizationalModelFilterSet` | +| `NestedGroupModel` | `netbox.filtersets.NestedGroupModelFilterSet` | + ### Declaring Filter Sets To utilize a filter set in a subclass of one of NetBox's generic views (such as `ObjectListView` or `BulkEditView`), define the `filterset` attribute on the view class: diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index eb12204ff..005248ae5 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -67,6 +67,24 @@ class MyModel(ExportTemplatesMixin, TagsMixin, models.Model): ... ``` +## Additional Models + +In addition to the base NetBoxModel class, the following additional classes are provided for convenience. + +!!! info "These base classes were added to the plugins API in NetBox v4.5." + +### PrimaryModel + +PrimaryModel is the go-to class for most object types. It extends NetBoxModel with `description` and `comments` fields, and it introduces support for ownership assignment. + +### OrganizationalModel + +OrganizationalModel is used by object types whose function is primarily the organization of other objects. It adds to NetBoxModel fields for `name`, `slug`, and `description`. It also supports ownership assignment. + +### NestedGroupModel + +NestedGroupModel is used for objects which arrange into a recursive hierarchy (like regions and locations) via its self-referential `parent` foreign key. It adds to NetBoxModel fields for `name`, `slug`, `description`, and `comments`. It also supports ownership assignment. + ## Database Migrations Once you have completed defining the model(s) for your plugin, you'll need to create the database schema migrations. A migration file is essentially a set of instructions for manipulating the PostgreSQL database to support your new model, or to alter existing models. Creating migrations can usually be done automatically using Django's `makemigrations` management command. (Ensure that your plugin has been installed and enabled first, otherwise it won't be found.) diff --git a/docs/plugins/development/rest-api.md b/docs/plugins/development/rest-api.md index 8cb5b3713..7b445d9a8 100644 --- a/docs/plugins/development/rest-api.md +++ b/docs/plugins/development/rest-api.md @@ -27,6 +27,14 @@ Serializers are responsible for converting Python objects to JSON data suitable The default nested representation of an object is defined by the `brief_fields` attributes under the serializer's `Meta` class. (Older versions of NetBox required the definition of a separate nested serializer.) +In addition to the base NetBoxModelSerializer class, the following serializer classes are also available for applicable models. + +| Model Class | Serializer Class | +|-----------------------|--------------------------------------------------------| +| `PrimaryModel` | `netbox.api.serializers.PrimaryModelSerializer` | +| `OrganizationalModel` | `netbox.api.serializers.OrganizationalModelSerializer` | +| `NestedGroupModel` | `netbox.api.serializers.NestedGroupModelSerializer` | + #### Example To create a serializer for a plugin model, subclass `NetBoxModelSerializer` in `api/serializers.py`. Specify the model class and the fields to include within the serializer's `Meta` class. diff --git a/docs/plugins/development/tables.md b/docs/plugins/development/tables.md index ea36b204d..70040f8ea 100644 --- a/docs/plugins/development/tables.md +++ b/docs/plugins/development/tables.md @@ -36,6 +36,14 @@ class MyModelTable(NetBoxTable): default_columns = ('pk', 'name', ...) ``` +In addition to the base NetBoxTable class, the following table classes are also available for applicable models. + +| Model Class | Serializer Class | +|-----------------------|------------------------------------------| +| `PrimaryModel` | `netbox.tables.PrimaryModelTable` | +| `OrganizationalModel` | `netbox.tables.OrganizationalModelTable` | +| `NestedGroupModel` | `netbox.tables.NestedGroupModelTable` | + ### Table Configuration The NetBoxTable class features dynamic configuration to allow users to change their column display and ordering preferences. To configure a table for a specific request, simply call its `configure()` method and pass the current HTTPRequest object. For example: From 1715ce6cdd7a752dc93cd0e5e0c1333fa5ae0821 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Dec 2025 15:29:34 -0500 Subject: [PATCH 2/5] Document remaining primary/organizational/nestedgroup resources for the plugins API (except GraphQL types & filters) --- docs/plugins/development/filtersets.md | 2 +- docs/plugins/development/forms.md | 52 +++++++++++++++++++++++--- docs/plugins/development/models.md | 36 ++++++++++++++---- docs/plugins/development/rest-api.md | 2 +- docs/plugins/development/tables.md | 2 +- 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index cfaf9316b..36e6346c5 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -32,7 +32,7 @@ class MyFilterSet(NetBoxModelFilterSet): fields = ('some', 'other', 'fields') ``` -In addition to the base NetBoxModelFilterSet class, the following filterset classes are also available for applicable models. +In addition to the base NetBoxModelFilterSet class, the following filterset classes are also available for subclasses of standard base models. | Model Class | FilterSet Class | |-----------------------|--------------------------------------------------| diff --git a/docs/plugins/development/forms.md b/docs/plugins/development/forms.md index 209506172..4be866987 100644 --- a/docs/plugins/development/forms.md +++ b/docs/plugins/development/forms.md @@ -2,7 +2,7 @@ ## Form Classes -NetBox provides several base form classes for use by plugins. +NetBox provides several base form classes for use by plugins. Additional form classes are also available for other standard base model classes (PrimaryModel, OrganizationalModel, and NestedGroupModel). | Form Class | Purpose | |----------------------------|--------------------------------------| @@ -19,7 +19,17 @@ This is the base form for creating and editing NetBox models. It extends Django' |-------------|---------------------------------------------------------------------------------------| | `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) | -**Example** +#### Subclasses + +The corresponding model-specific subclasses of `NetBoxModelForm` are documented below. + +| Model Class | Form Class | +|---------------------|-------------------------| +| PrimaryModel | PrimaryModelForm | +| OrganizationalModel | OrganizationalModelForm | +| NestedGroupModel | NestedGroupModelForm | + +#### Example ```python from django.utils.translation import gettext_lazy as _ @@ -49,9 +59,19 @@ class MyModelForm(NetBoxModelForm): ### `NetBoxModelImportForm` -This form facilitates the bulk import of new objects from CSV, JSON, or YAML data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for import various types of CSV data, listed below. +This form facilitates the bulk import of new objects from CSV, JSON, or YAML data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for importing various types of CSV data, listed [below](#csv-import-fields). -**Example** +#### Subclasses + +The corresponding model-specific subclasses of `NetBoxModelImportForm` are documented below. + +| Model Class | Form Class | +|---------------------|-------------------------------| +| PrimaryModel | PrimaryModelImportForm | +| OrganizationalModel | OrganizationalModelImportForm | +| NestedGroupModel | NestedGroupModelImportForm | + +#### Example ```python from django.utils.translation import gettext_lazy as _ @@ -83,7 +103,17 @@ This form facilitates editing multiple objects in bulk. Unlike a model form, thi | `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) | | `nullable_fields` | A tuple of fields which can be nullified (set to empty) using the bulk edit form (optional) | -**Example** +#### Subclasses + +The corresponding model-specific subclasses of `NetBoxModelBulkEditForm` are documented below. + +| Model Class | Form Class | +|---------------------|---------------------------------| +| PrimaryModel | PrimaryModelBulkEditForm | +| OrganizationalModel | OrganizationalModelBulkEditForm | +| NestedGroupModel | NestedGroupModelBulkEditForm | + +#### Example ```python from django import forms @@ -125,7 +155,17 @@ This form class is used to render a form expressly for filtering a list of objec | `model` | The model of object being edited | | `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) | -**Example** +#### Subclasses + +The corresponding model-specific subclasses of `NetBoxModelFilterSetForm` are documented below. + +| Model Class | Form Class | +|---------------------|----------------------------------| +| PrimaryModel | PrimaryModelFilterSetForm | +| OrganizationalModel | OrganizationalModelFilterSetForm | +| NestedGroupModel | NestedGroupModelFilterSetForm | + +#### Example ```python from dcim.models import Site diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index 005248ae5..9a8256d6d 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -67,23 +67,45 @@ class MyModel(ExportTemplatesMixin, TagsMixin, models.Model): ... ``` -## Additional Models +### Additional Models In addition to the base NetBoxModel class, the following additional classes are provided for convenience. -!!! info "These base classes were added to the plugins API in NetBox v4.5." +!!! info "These model classes were added to the plugins API in NetBox v4.5." -### PrimaryModel +#### PrimaryModel PrimaryModel is the go-to class for most object types. It extends NetBoxModel with `description` and `comments` fields, and it introduces support for ownership assignment. -### OrganizationalModel +| Field | Required | Unique | Description | +|---------------|----------|--------|---------------------------------------------| +| `owner` | No | No | The object's owner | +| `description` | No | No | A human-friendly description for the object | +| `comments` | No | No | General comments | -OrganizationalModel is used by object types whose function is primarily the organization of other objects. It adds to NetBoxModel fields for `name`, `slug`, and `description`. It also supports ownership assignment. +#### OrganizationalModel -### NestedGroupModel +OrganizationalModel is used by object types whose function is primarily the organization of other objects. -NestedGroupModel is used for objects which arrange into a recursive hierarchy (like regions and locations) via its self-referential `parent` foreign key. It adds to NetBoxModel fields for `name`, `slug`, `description`, and `comments`. It also supports ownership assignment. +| Field | Required | Unique | Description | +|---------------|----------|--------|---------------------------------------------| +| `name` | Yes | Yes | The name of the object | +| `slug` | Yes | Yes | A unique URL-friendly identifier | +| `owner` | No | No | The object's owner | +| `description` | No | No | A human-friendly description for the object | + +#### NestedGroupModel + +NestedGroupModel is used for objects which arrange into a recursive hierarchy (like regions and locations) via its self-referential `parent` foreign key. + +| Field | Required | Unique | Description | +|---------------|----------|--------|-----------------------------------------------------------------| +| `name` | Yes | Yes | The name of the object | +| `slug` | Yes | Yes | A unique URL-friendly identifier | +| `parent` | No | No | The object (of the same type) under which this object is nested | +| `owner` | No | No | The object's owner | +| `description` | No | No | A human-friendly description for the object | +| `comments` | No | No | General comments | ## Database Migrations diff --git a/docs/plugins/development/rest-api.md b/docs/plugins/development/rest-api.md index 7b445d9a8..875289d94 100644 --- a/docs/plugins/development/rest-api.md +++ b/docs/plugins/development/rest-api.md @@ -27,7 +27,7 @@ Serializers are responsible for converting Python objects to JSON data suitable The default nested representation of an object is defined by the `brief_fields` attributes under the serializer's `Meta` class. (Older versions of NetBox required the definition of a separate nested serializer.) -In addition to the base NetBoxModelSerializer class, the following serializer classes are also available for applicable models. +In addition to the base NetBoxModelSerializer class, the following serializer classes are also available for subclasses of standard base models. | Model Class | Serializer Class | |-----------------------|--------------------------------------------------------| diff --git a/docs/plugins/development/tables.md b/docs/plugins/development/tables.md index 70040f8ea..ce7def26d 100644 --- a/docs/plugins/development/tables.md +++ b/docs/plugins/development/tables.md @@ -36,7 +36,7 @@ class MyModelTable(NetBoxTable): default_columns = ('pk', 'name', ...) ``` -In addition to the base NetBoxTable class, the following table classes are also available for applicable models. +In addition to the base NetBoxTable class, the following table classes are also available for subclasses of standard base models. | Model Class | Serializer Class | |-----------------------|------------------------------------------| From 16c8dffe7c50440e4b521b17ca0598670b6f2355 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Dec 2025 15:30:02 -0500 Subject: [PATCH 3/5] Refactor filterset forms to use OwnerFilterMixin --- netbox/netbox/forms/filtersets.py | 32 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/netbox/netbox/forms/filtersets.py b/netbox/netbox/forms/filtersets.py index 18c4ef548..53530ea6e 100644 --- a/netbox/netbox/forms/filtersets.py +++ b/netbox/netbox/forms/filtersets.py @@ -45,34 +45,30 @@ class NetBoxModelFilterSetForm(FilterModifierMixin, CustomFieldsMixin, SavedFilt return customfield.to_form_field(set_initial=False, enforce_required=False, enforce_visibility=False) -class PrimaryModelFilterSetForm(NetBoxModelFilterSetForm): +class OwnerFilterMixin(forms.Form): + owner_id = DynamicModelChoiceField( + queryset=Owner.objects.all(), + required=False, + label=_('Owner'), + ) + + +class PrimaryModelFilterSetForm(OwnerFilterMixin, NetBoxModelFilterSetForm): """ FilterSet form for models which inherit from PrimaryModel. """ - owner_id = DynamicModelChoiceField( - queryset=Owner.objects.all(), - required=False, - label=_('Owner'), - ) + pass -class OrganizationalModelFilterSetForm(NetBoxModelFilterSetForm): +class OrganizationalModelFilterSetForm(OwnerFilterMixin, NetBoxModelFilterSetForm): """ FilterSet form for models which inherit from OrganizationalModel. """ - owner_id = DynamicModelChoiceField( - queryset=Owner.objects.all(), - required=False, - label=_('Owner'), - ) + pass -class NestedGroupModelFilterSetForm(NetBoxModelFilterSetForm): +class NestedGroupModelFilterSetForm(OwnerFilterMixin, NetBoxModelFilterSetForm): """ FilterSet form for models which inherit from NestedGroupModel. """ - owner_id = DynamicModelChoiceField( - queryset=Owner.objects.all(), - required=False, - label=_('Owner'), - ) + pass From 6dc00206a485e22054a02185134626ee2beeb623 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 8 Dec 2025 15:13:19 -0500 Subject: [PATCH 4/5] Add GraphQL filter classes --- docs/plugins/development/graphql-api.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/plugins/development/graphql-api.md b/docs/plugins/development/graphql-api.md index 603b0cead..b7691cc70 100644 --- a/docs/plugins/development/graphql-api.md +++ b/docs/plugins/development/graphql-api.md @@ -46,3 +46,19 @@ NetBox provides two object type classes for use by plugins. ::: netbox.graphql.types.NetBoxObjectType options: members: false + +## GraphQL Filters + +NetBox provides a base filter class for use by plugins which employ subclasseses of `NetBoxModel`. + +::: netbox.graphql.filters.NetBoxModelFilter + options: + members: false + +Additionally, the following filter classes are available for subclasses of standard base models. + +| Model Class | FilterSet Class | +|-----------------------|----------------------------------------------------| +| `PrimaryModel` | `netbox.graphql.filters.PrimaryModelFilter` | +| `OrganizationalModel` | `netbox.graphql.filters.OrganizationalModelFilter` | +| `NestedGroupModel` | `netbox.graphql.filters.NestedGroupModelFilter` | From 33171693293d92079a190428de76d2a83ec054d3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 8 Dec 2025 15:17:03 -0500 Subject: [PATCH 5/5] Clean up formatting --- docs/plugins/development/forms.md | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/plugins/development/forms.md b/docs/plugins/development/forms.md index 4be866987..afe05407e 100644 --- a/docs/plugins/development/forms.md +++ b/docs/plugins/development/forms.md @@ -23,11 +23,11 @@ This is the base form for creating and editing NetBox models. It extends Django' The corresponding model-specific subclasses of `NetBoxModelForm` are documented below. -| Model Class | Form Class | -|---------------------|-------------------------| -| PrimaryModel | PrimaryModelForm | -| OrganizationalModel | OrganizationalModelForm | -| NestedGroupModel | NestedGroupModelForm | +| Model Class | Form Class | +|-----------------------|---------------------------| +| `PrimaryModel` | `PrimaryModelForm` | +| `OrganizationalModel` | `OrganizationalModelForm` | +| `NestedGroupModel` | `NestedGroupModelForm` | #### Example @@ -65,11 +65,11 @@ This form facilitates the bulk import of new objects from CSV, JSON, or YAML dat The corresponding model-specific subclasses of `NetBoxModelImportForm` are documented below. -| Model Class | Form Class | -|---------------------|-------------------------------| -| PrimaryModel | PrimaryModelImportForm | -| OrganizationalModel | OrganizationalModelImportForm | -| NestedGroupModel | NestedGroupModelImportForm | +| Model Class | Form Class | +|-----------------------|---------------------------------| +| `PrimaryModel` | `PrimaryModelImportForm` | +| `OrganizationalModel` | `OrganizationalModelImportForm` | +| `NestedGroupModel` | `NestedGroupModelImportForm` | #### Example @@ -107,11 +107,11 @@ This form facilitates editing multiple objects in bulk. Unlike a model form, thi The corresponding model-specific subclasses of `NetBoxModelBulkEditForm` are documented below. -| Model Class | Form Class | -|---------------------|---------------------------------| -| PrimaryModel | PrimaryModelBulkEditForm | -| OrganizationalModel | OrganizationalModelBulkEditForm | -| NestedGroupModel | NestedGroupModelBulkEditForm | +| Model Class | Form Class | +|-----------------------|-----------------------------------| +| `PrimaryModel` | `PrimaryModelBulkEditForm` | +| `OrganizationalModel` | `OrganizationalModelBulkEditForm` | +| `NestedGroupModel` | `NestedGroupModelBulkEditForm` | #### Example @@ -159,11 +159,11 @@ This form class is used to render a form expressly for filtering a list of objec The corresponding model-specific subclasses of `NetBoxModelFilterSetForm` are documented below. -| Model Class | Form Class | -|---------------------|----------------------------------| -| PrimaryModel | PrimaryModelFilterSetForm | -| OrganizationalModel | OrganizationalModelFilterSetForm | -| NestedGroupModel | NestedGroupModelFilterSetForm | +| Model Class | Form Class | +|-----------------------|------------------------------------| +| `PrimaryModel` | `PrimaryModelFilterSetForm` | +| `OrganizationalModel` | `OrganizationalModelFilterSetForm` | +| `NestedGroupModel` | `NestedGroupModelFilterSetForm` | #### Example