mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-26 18:38:38 -06:00
Update to latest feature
This commit is contained in:
commit
b12ce97474
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -26,7 +26,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.0.5
|
placeholder: v4.0.6
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@ -14,7 +14,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.0.5
|
placeholder: v4.0.6
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -5,10 +5,12 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'contrib/**'
|
- 'contrib/**'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
- 'netbox/translations/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'contrib/**'
|
- 'contrib/**'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
- 'netbox/translations/**'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ netbox.pid
|
|||||||
.idea
|
.idea
|
||||||
.coverage
|
.coverage
|
||||||
.vscode
|
.vscode
|
||||||
|
.python-version
|
||||||
|
@ -17,7 +17,6 @@ NetBox exists to empower network engineers. Since its release in 2016, it has be
|
|||||||
<a href="#why-netbox">Why NetBox?</a> |
|
<a href="#why-netbox">Why NetBox?</a> |
|
||||||
<a href="#getting-started">Getting Started</a> |
|
<a href="#getting-started">Getting Started</a> |
|
||||||
<a href="#get-involved">Get Involved</a> |
|
<a href="#get-involved">Get Involved</a> |
|
||||||
<a href="#project-stats">Project Stats</a> |
|
|
||||||
<a href="#screenshots">Screenshots</a>
|
<a href="#screenshots">Screenshots</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ If you believe you've uncovered a security vulnerability and wish to report it c
|
|||||||
|
|
||||||
Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous.
|
Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous.
|
||||||
|
|
||||||
If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/netbox-community/netbox/security/advisories/new) on GitHub, or email a brief description of the suspected bug and instructions for reproduction to **security@netbox.dev**. For any security concerns regarding NetBox deployed via Docker, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project.
|
If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/netbox-community/netbox/security/advisories/new) on GitHub. For any security concerns regarding NetBox deployed via Docker, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project.
|
||||||
|
|
||||||
### Bug Bounties
|
### Bug Bounties
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ django-cors-headers
|
|||||||
|
|
||||||
# Runtime UI tool for debugging Django
|
# Runtime UI tool for debugging Django
|
||||||
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
|
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
|
||||||
django-debug-toolbar
|
# Pinned for DNS looukp bug; see https://github.com/netbox-community/netbox/issues/16454
|
||||||
|
# and https://github.com/jazzband/django-debug-toolbar/issues/1927
|
||||||
|
django-debug-toolbar==4.3.0
|
||||||
|
|
||||||
# Library for writing reusable URL query filters
|
# Library for writing reusable URL query filters
|
||||||
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
|
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
|
||||||
@ -108,7 +110,7 @@ Pillow
|
|||||||
|
|
||||||
# PostgreSQL database adapter for Python
|
# PostgreSQL database adapter for Python
|
||||||
# https://github.com/psycopg/psycopg/blob/master/docs/news.rst
|
# https://github.com/psycopg/psycopg/blob/master/docs/news.rst
|
||||||
psycopg[binary,pool]
|
psycopg[c,pool]
|
||||||
|
|
||||||
# YAML rendering library
|
# YAML rendering library
|
||||||
# https://github.com/yaml/pyyaml/blob/master/CHANGES
|
# https://github.com/yaml/pyyaml/blob/master/CHANGES
|
||||||
|
95605
contrib/openapi2.json
95605
contrib/openapi2.json
File diff suppressed because it is too large
Load Diff
69695
contrib/openapi2.yaml
69695
contrib/openapi2.yaml
File diff suppressed because it is too large
Load Diff
@ -138,11 +138,11 @@ These two methods will load data in YAML or JSON format, respectively, from file
|
|||||||
|
|
||||||
The Script object provides a set of convenient functions for recording messages at different severity levels:
|
The Script object provides a set of convenient functions for recording messages at different severity levels:
|
||||||
|
|
||||||
* `log_debug(message, object=None)`
|
* `log_debug(message=None, obj=None)`
|
||||||
* `log_success(message, object=None)`
|
* `log_success(message=None, obj=None)`
|
||||||
* `log_info(message, object=None)`
|
* `log_info(message=None, obj=None)`
|
||||||
* `log_warning(message, object=None)`
|
* `log_warning(message=None, obj=None)`
|
||||||
* `log_failure(message, object=None)`
|
* `log_failure(message=None, obj=None)`
|
||||||
|
|
||||||
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages. A message may optionally be associated with a particular object by passing it as the second argument to the logging method.
|
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages. A message may optionally be associated with a particular object by passing it as the second argument to the logging method.
|
||||||
|
|
||||||
@ -152,6 +152,8 @@ A script can define one or more test methods to report on certain conditions. Al
|
|||||||
|
|
||||||
These methods are detected and run automatically when the script is executed, unless its `run()` method has been overridden. (When overriding `run()`, `run_tests()` can be called to run all test methods present in the script.)
|
These methods are detected and run automatically when the script is executed, unless its `run()` method has been overridden. (When overriding `run()`, `run_tests()` can be called to run all test methods present in the script.)
|
||||||
|
|
||||||
|
Calling any of these logging methods without a message will increment the relevant counter, but will not generate an output line in the script's log.
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
This functionality was ported from [legacy reports](./reports.md) in NetBox v4.0.
|
This functionality was ported from [legacy reports](./reports.md) in NetBox v4.0.
|
||||||
|
|
||||||
|
@ -126,3 +126,13 @@ VERSION = 'v3.3.2-dev'
|
|||||||
```
|
```
|
||||||
|
|
||||||
Commit this change with the comment "PRVB" (for _post-release version bump_) and push the commit upstream.
|
Commit this change with the comment "PRVB" (for _post-release version bump_) and push the commit upstream.
|
||||||
|
|
||||||
|
### Update the Public Documentation
|
||||||
|
|
||||||
|
After a release has been published, the public NetBox documentation needs to be updated. This is accomplished by running two actions on the [netboxlabs-docs](https://github.com/netboxlabs/netboxlabs-docs) repository.
|
||||||
|
|
||||||
|
First, run the `build-site` action, by navigating to Actions > build-site > Run workflow. This process compiles the documentation along with an overlay for integration with the documentation portal at <https://netboxlabs.com/docs>. The job should take about two minutes.
|
||||||
|
|
||||||
|
Once the documentation files have been compiled, they must be published by running the `deploy-kinsta` action. Select the desired deployment environment (staging or production) and specify `latest` as the deploy tag.
|
||||||
|
|
||||||
|
Finally, verify that the documentation at <https://netboxlabs.com/docs/netbox/en/stable/> has been updated.
|
||||||
|
@ -107,3 +107,7 @@ For numeric custom fields only. The maximum valid value (optional).
|
|||||||
### Validation Regex
|
### Validation Regex
|
||||||
|
|
||||||
For string-based custom fields only. A regular expression used to validate the field's value (optional).
|
For string-based custom fields only. A regular expression used to validate the field's value (optional).
|
||||||
|
|
||||||
|
### Uniqueness Validation
|
||||||
|
|
||||||
|
If enabled, each object must have a unique value set for this custom field (per object type).
|
||||||
|
@ -40,3 +40,7 @@ The security cipher used to apply wireless authentication. Options include:
|
|||||||
### Pre-Shared Key
|
### Pre-Shared Key
|
||||||
|
|
||||||
The security key configured on each client to grant access to the secured wireless LAN. This applies only to certain authentication types.
|
The security key configured on each client to grant access to the secured wireless LAN. This applies only to certain authentication types.
|
||||||
|
|
||||||
|
### Distance
|
||||||
|
|
||||||
|
The numeric distance of the link, including a unit designation (e.g. 100 meters or 25 feet).
|
||||||
|
@ -27,7 +27,7 @@ Serializers are responsible for converting Python objects to JSON data suitable
|
|||||||
|
|
||||||
#### Example
|
#### 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. It is generally advisable to include a `url` attribute on each serializer. This will render the direct link to access the object being rendered.
|
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.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# api/serializers.py
|
# api/serializers.py
|
||||||
@ -36,9 +36,7 @@ from netbox.api.serializers import NetBoxModelSerializer
|
|||||||
from my_plugin.models import MyModel
|
from my_plugin.models import MyModel
|
||||||
|
|
||||||
class MyModelSerializer(NetBoxModelSerializer):
|
class MyModelSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(
|
foo = SiteSerializer(nested=True, allow_null=True)
|
||||||
view_name='plugins-api:myplugin-api:mymodel-detail'
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MyModel
|
model = MyModel
|
||||||
@ -63,9 +61,7 @@ from netbox.api.serializers import WritableNestedSerializer
|
|||||||
from my_plugin.models import MyModel
|
from my_plugin.models import MyModel
|
||||||
|
|
||||||
class NestedMyModelSerializer(WritableNestedSerializer):
|
class NestedMyModelSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(
|
foo = SiteSerializer(nested=True, allow_null=True)
|
||||||
view_name='plugins-api:myplugin-api:mymodel-detail'
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MyModel
|
model = MyModel
|
||||||
|
@ -191,7 +191,7 @@ class MyView(generic.ObjectView):
|
|||||||
|
|
||||||
### Extra Template Content
|
### Extra Template Content
|
||||||
|
|
||||||
Plugins can inject custom content into certain areas of core NetBox views. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired method(s) to render custom content. Five methods are available:
|
Plugins can inject custom content into certain areas of core NetBox views. This is accomplished by subclassing `PluginTemplateExtension`, optionally designating one or more particular NetBox models, and defining the desired method(s) to render custom content. Five methods are available:
|
||||||
|
|
||||||
| Method | View | Description |
|
| Method | View | Description |
|
||||||
|---------------------|-------------|-----------------------------------------------------|
|
|---------------------|-------------|-----------------------------------------------------|
|
||||||
@ -206,7 +206,9 @@ Plugins can inject custom content into certain areas of core NetBox views. This
|
|||||||
|
|
||||||
Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however.
|
Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however.
|
||||||
|
|
||||||
When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data include:
|
To control where the custom content is injected, plugin authors can specify an iterable of models by overriding the `models` attribute on the subclass. Extensions which do not specify a set of models will be invoked on every view, where supported.
|
||||||
|
|
||||||
|
When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data includes:
|
||||||
|
|
||||||
* `object` - The object being viewed (object views only)
|
* `object` - The object being viewed (object views only)
|
||||||
* `model` - The model of the list view (list views only)
|
* `model` - The model of the list view (list views only)
|
||||||
@ -223,7 +225,7 @@ from netbox.plugins import PluginTemplateExtension
|
|||||||
from .models import Animal
|
from .models import Animal
|
||||||
|
|
||||||
class SiteAnimalCount(PluginTemplateExtension):
|
class SiteAnimalCount(PluginTemplateExtension):
|
||||||
model = 'dcim.site'
|
models = ['dcim.site']
|
||||||
|
|
||||||
def right_page(self):
|
def right_page(self):
|
||||||
return self.render('netbox_animal_sounds/inc/animal_count.html', extra_context={
|
return self.render('netbox_animal_sounds/inc/animal_count.html', extra_context={
|
||||||
|
@ -1,6 +1,36 @@
|
|||||||
# NetBox v4.0
|
# NetBox v4.0
|
||||||
|
|
||||||
## v4.0.6 (FUTURE)
|
## v4.0.7 (FUTURE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v4.0.6 (2024-06-24)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#15348](https://github.com/netbox-community/netbox/issues/15348) - Show saved filters alongside quick search on object list views
|
||||||
|
* [#15794](https://github.com/netbox-community/netbox/issues/15794) - Dynamically populate related objects in UI views
|
||||||
|
* [#16256](https://github.com/netbox-community/netbox/issues/16256) - Enable alphabetical ordering of bookmarks on dashboard
|
||||||
|
* [#16307](https://github.com/netbox-community/netbox/issues/16307) - Enable calling `log_*()` methods on Script without passing a message
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#13925](https://github.com/netbox-community/netbox/issues/13925) - Fix support for "zulu" (UTC) timestamps for custom fields
|
||||||
|
* [#14829](https://github.com/netbox-community/netbox/issues/14829) - Fix support for simple conditions (without AND/OR) in event rules
|
||||||
|
* [#15717](https://github.com/netbox-community/netbox/issues/15717) - Allow assigning a device/VM in a site to a cluster with no site assigned
|
||||||
|
* [#16143](https://github.com/netbox-community/netbox/issues/16143) - Display timestamps in tables in the configured timezone
|
||||||
|
* [#16149](https://github.com/netbox-community/netbox/issues/16149) - Fix object linking in custom script logs
|
||||||
|
* [#16252](https://github.com/netbox-community/netbox/issues/16252) - Fix total count in tab at top of rack elevations view
|
||||||
|
* [#16273](https://github.com/netbox-community/netbox/issues/16273) - Restore global search bar on mobile
|
||||||
|
* [#16416](https://github.com/netbox-community/netbox/issues/16416) - Retain dark/light mode toggle on mobile view
|
||||||
|
* [#16444](https://github.com/netbox-community/netbox/issues/16444) - Disable ordering circuits list by A/Z termination
|
||||||
|
* [#16450](https://github.com/netbox-community/netbox/issues/16450) - Searching for rack unit in form dropdown should be case-insensitive
|
||||||
|
* [#16452](https://github.com/netbox-community/netbox/issues/16452) - Fix sizing of buttons within object attribute panels
|
||||||
|
* [#16454](https://github.com/netbox-community/netbox/issues/16454) - Address DNS lookup bug in `django-debug-toolbar
|
||||||
|
* [#16460](https://github.com/netbox-community/netbox/issues/16460) - Omit spaces from telephone number URLs
|
||||||
|
* [#16512](https://github.com/netbox-community/netbox/issues/16512) - Restore a user's preferred language (if any) on login
|
||||||
|
* [#16542](https://github.com/netbox-community/netbox/issues/16542) - Fix bulk form operations when HTMX is enabled
|
||||||
|
* [#16702](https://github.com/netbox-community/netbox/issues/16702) - Fix validation of `return_url` query parameter
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
30
docs/release-notes/version-4.1.md
Normal file
30
docs/release-notes/version-4.1.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# NetBox v4.1
|
||||||
|
|
||||||
|
## v4.1.0 (FUTURE)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Several filters deprecated in v4.0 have been removed (see [#15410](https://github.com/netbox-community/netbox/issues/15410)).
|
||||||
|
* The unit size for virtual disk size has been changed from 1 gigabyte to 1 megabyte. Existing values have been updated accordingly.
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#7537](https://github.com/netbox-community/netbox/issues/7537) - Add a serial number field for virtual machines
|
||||||
|
* [#16359](https://github.com/netbox-community/netbox/issues/16359) - Enable plugins to embed content in the top navigation bar
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
* [#14692](https://github.com/netbox-community/netbox/issues/14692) - Change atomic unit for virtual disks from 1GB to 1MB
|
||||||
|
* [#15410](https://github.com/netbox-community/netbox/issues/15410) - Removed various deprecated filters
|
||||||
|
* [#15908](https://github.com/netbox-community/netbox/issues/15908) - Indicate product edition in release data
|
||||||
|
* [#16388](https://github.com/netbox-community/netbox/issues/16388) - Move all change logging resources from `extras` to `core`
|
||||||
|
|
||||||
|
### REST API Changes
|
||||||
|
|
||||||
|
* The `/api/extras/object-changes/` endpoint has moved to `/api/core/object-changes/`
|
||||||
|
* virtualization.VirtualMachine
|
||||||
|
* Added the optional `serial` field
|
||||||
|
* wireless.WirelessLink
|
||||||
|
* Added the optional `distance` and `distance_unit` fields
|
@ -296,6 +296,7 @@ nav:
|
|||||||
- git Cheat Sheet: 'development/git-cheat-sheet.md'
|
- git Cheat Sheet: 'development/git-cheat-sheet.md'
|
||||||
- Release Notes:
|
- Release Notes:
|
||||||
- Summary: 'release-notes/index.md'
|
- Summary: 'release-notes/index.md'
|
||||||
|
- Version 4.1: 'release-notes/version-4.1.md'
|
||||||
- Version 4.0: 'release-notes/version-4.0.md'
|
- Version 4.0: 'release-notes/version-4.0.md'
|
||||||
- Version 3.7: 'release-notes/version-3.7.md'
|
- Version 3.7: 'release-notes/version-3.7.md'
|
||||||
- Version 3.6: 'release-notes/version-3.6.md'
|
- Version 3.6: 'release-notes/version-3.6.md'
|
||||||
|
@ -19,8 +19,10 @@ from django.views.generic import View
|
|||||||
from social_core.backends.utils import load_backends
|
from social_core.backends.utils import load_backends
|
||||||
|
|
||||||
from account.models import UserToken
|
from account.models import UserToken
|
||||||
from extras.models import Bookmark, ObjectChange
|
from core.models import ObjectChange
|
||||||
from extras.tables import BookmarkTable, ObjectChangeTable
|
from core.tables import ObjectChangeTable
|
||||||
|
from extras.models import Bookmark
|
||||||
|
from extras.tables import BookmarkTable
|
||||||
from netbox.authentication import get_auth_backend_display, get_saml_idps
|
from netbox.authentication import get_auth_backend_display, get_saml_idps
|
||||||
from netbox.config import get_config
|
from netbox.config import get_config
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
@ -104,10 +106,16 @@ class LoginView(View):
|
|||||||
# Ensure the user has a UserConfig defined. (This should normally be handled by
|
# Ensure the user has a UserConfig defined. (This should normally be handled by
|
||||||
# create_userconfig() on user creation.)
|
# create_userconfig() on user creation.)
|
||||||
if not hasattr(request.user, 'config'):
|
if not hasattr(request.user, 'config'):
|
||||||
config = get_config()
|
request.user.config = get_config()
|
||||||
UserConfig(user=request.user, data=config.DEFAULT_USER_PREFERENCES).save()
|
UserConfig(user=request.user, data=request.user.config.DEFAULT_USER_PREFERENCES).save()
|
||||||
|
|
||||||
return self.redirect_to_next(request, logger)
|
response = self.redirect_to_next(request, logger)
|
||||||
|
|
||||||
|
# Set the user's preferred language (if any)
|
||||||
|
if language := request.user.config.get('locale.language'):
|
||||||
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Login form validation failed for username: {form['username'].value()}")
|
logger.debug(f"Login form validation failed for username: {form['username'].value()}")
|
||||||
@ -145,9 +153,10 @@ class LogoutView(View):
|
|||||||
logger.info(f"User {username} has logged out")
|
logger.info(f"User {username} has logged out")
|
||||||
messages.info(request, "You have logged out.")
|
messages.info(request, "You have logged out.")
|
||||||
|
|
||||||
# Delete session key cookie (if set) upon logout
|
# Delete session key & language cookies (if set) upon logout
|
||||||
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
|
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
|
||||||
response.delete_cookie('session_key')
|
response.delete_cookie('session_key')
|
||||||
|
response.delete_cookie(settings.LANGUAGE_COOKIE_NAME)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -20,11 +20,10 @@ __all__ = [
|
|||||||
#
|
#
|
||||||
|
|
||||||
class NestedProviderNetworkSerializer(WritableNestedSerializer):
|
class NestedProviderNetworkSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProviderNetwork
|
model = ProviderNetwork
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -35,12 +34,11 @@ class NestedProviderNetworkSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('circuit_count',),
|
exclude_fields=('circuit_count',),
|
||||||
)
|
)
|
||||||
class NestedProviderSerializer(WritableNestedSerializer):
|
class NestedProviderSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
|
||||||
circuit_count = RelatedObjectCountField('circuits')
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Provider
|
model = Provider
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'circuit_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -48,11 +46,10 @@ class NestedProviderSerializer(WritableNestedSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class NestedProviderAccountSerializer(WritableNestedSerializer):
|
class NestedProviderAccountSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProviderAccount
|
model = ProviderAccount
|
||||||
fields = ['id', 'url', 'display', 'name', 'account']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'account']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -63,26 +60,23 @@ class NestedProviderAccountSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('circuit_count',),
|
exclude_fields=('circuit_count',),
|
||||||
)
|
)
|
||||||
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
|
||||||
circuit_count = RelatedObjectCountField('circuits')
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'circuit_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedCircuitSerializer(WritableNestedSerializer):
|
class NestedCircuitSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = ['id', 'url', 'display', 'cid']
|
fields = ['id', 'url', 'display_url', 'display', 'cid']
|
||||||
|
|
||||||
|
|
||||||
class NestedCircuitTerminationSerializer(WritableNestedSerializer):
|
class NestedCircuitTerminationSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
|
||||||
circuit = NestedCircuitSerializer()
|
circuit = NestedCircuitSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = ['id', 'url', 'display', 'circuit', 'term_side', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'cable', '_occupied']
|
||||||
|
@ -18,7 +18,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class CircuitTypeSerializer(NetBoxModelSerializer):
|
class CircuitTypeSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
circuit_count = RelatedObjectCountField('circuits')
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
@ -26,27 +25,25 @@ class CircuitTypeSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
|
||||||
'last_updated', 'circuit_count',
|
'created', 'last_updated', 'circuit_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
|
||||||
|
|
||||||
|
|
||||||
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
|
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
|
||||||
site = SiteSerializer(nested=True, allow_null=True)
|
site = SiteSerializer(nested=True, allow_null=True)
|
||||||
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
|
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
|
'id', 'url', 'display_url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed',
|
||||||
'description',
|
'xconnect_id', 'description',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CircuitSerializer(NetBoxModelSerializer):
|
class CircuitSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
|
||||||
provider = ProviderSerializer(nested=True)
|
provider = ProviderSerializer(nested=True)
|
||||||
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
|
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
status = ChoiceField(choices=CircuitStatusChoices, required=False)
|
status = ChoiceField(choices=CircuitStatusChoices, required=False)
|
||||||
@ -58,15 +55,14 @@ class CircuitSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date',
|
'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant',
|
||||||
'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags',
|
'install_date', 'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'cid', 'description')
|
brief_fields = ('id', 'url', 'display', 'cid', 'description')
|
||||||
|
|
||||||
|
|
||||||
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
|
||||||
circuit = CircuitSerializer(nested=True)
|
circuit = CircuitSerializer(nested=True)
|
||||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||||
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
|
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
|
||||||
@ -74,8 +70,8 @@ class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
|
'id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed',
|
||||||
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end',
|
||||||
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied')
|
brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied')
|
||||||
|
@ -15,7 +15,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ProviderSerializer(NetBoxModelSerializer):
|
class ProviderSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
|
||||||
accounts = SerializedPKRelatedField(
|
accounts = SerializedPKRelatedField(
|
||||||
queryset=ProviderAccount.objects.all(),
|
queryset=ProviderAccount.objects.all(),
|
||||||
serializer=NestedProviderAccountSerializer,
|
serializer=NestedProviderAccountSerializer,
|
||||||
@ -36,34 +35,32 @@ class ProviderSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Provider
|
model = Provider
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'accounts', 'description', 'comments',
|
||||||
'custom_fields', 'created', 'last_updated', 'circuit_count',
|
'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
|
||||||
|
|
||||||
|
|
||||||
class ProviderAccountSerializer(NetBoxModelSerializer):
|
class ProviderAccountSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
|
|
||||||
provider = ProviderSerializer(nested=True)
|
provider = ProviderSerializer(nested=True)
|
||||||
name = serializers.CharField(allow_blank=True, max_length=100, required=False, default='')
|
name = serializers.CharField(allow_blank=True, max_length=100, required=False, default='')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProviderAccount
|
model = ProviderAccount
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags', 'custom_fields',
|
'id', 'url', 'display_url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags',
|
||||||
'created', 'last_updated',
|
'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ProviderNetworkSerializer(NetBoxModelSerializer):
|
class ProviderNetworkSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
|
|
||||||
provider = ProviderSerializer(nested=True)
|
provider = ProviderSerializer(nested=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProviderNetwork
|
model = ProviderNetwork
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
|
'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
@ -63,10 +63,12 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
status = columns.ChoiceFieldColumn()
|
status = columns.ChoiceFieldColumn()
|
||||||
termination_a = tables.TemplateColumn(
|
termination_a = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTERMINATION_LINK,
|
template_code=CIRCUITTERMINATION_LINK,
|
||||||
|
orderable=False,
|
||||||
verbose_name=_('Side A')
|
verbose_name=_('Side A')
|
||||||
)
|
)
|
||||||
termination_z = tables.TemplateColumn(
|
termination_z = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTERMINATION_LINK,
|
template_code=CIRCUITTERMINATION_LINK,
|
||||||
|
orderable=False,
|
||||||
verbose_name=_('Side Z')
|
verbose_name=_('Side Z')
|
||||||
)
|
)
|
||||||
commit_rate = CommitRateColumn(
|
commit_rate = CommitRateColumn(
|
||||||
|
@ -7,7 +7,7 @@ from netbox.views import generic
|
|||||||
from tenancy.views import ObjectContactsView
|
from tenancy.views import ObjectContactsView
|
||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.query import count_related
|
from utilities.query import count_related
|
||||||
from utilities.views import register_model_view
|
from utilities.views import GetRelatedModelsMixin, register_model_view
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
@ -26,17 +26,12 @@ class ProviderListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Provider)
|
@register_model_view(Provider)
|
||||||
class ProviderView(generic.ObjectView):
|
class ProviderView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Provider.objects.all()
|
queryset = Provider.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(ProviderAccount.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'),
|
|
||||||
(Circuit.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -92,16 +87,12 @@ class ProviderAccountListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(ProviderAccount)
|
@register_model_view(ProviderAccount)
|
||||||
class ProviderAccountView(generic.ObjectView):
|
class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = ProviderAccount.objects.all()
|
queryset = ProviderAccount.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Circuit.objects.restrict(request.user, 'view').filter(provider_account=instance), 'provider_account_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -156,19 +147,21 @@ class ProviderNetworkListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(ProviderNetwork)
|
@register_model_view(ProviderNetwork)
|
||||||
class ProviderNetworkView(generic.ObjectView):
|
class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = ProviderNetwork.objects.all()
|
queryset = ProviderNetwork.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
return {
|
||||||
|
'related_models': self.get_related_models(
|
||||||
|
request,
|
||||||
|
instance,
|
||||||
|
extra=(
|
||||||
(
|
(
|
||||||
Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance),
|
Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance),
|
||||||
'provider_network_id',
|
'provider_network_id',
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
return {
|
|
||||||
'related_models': related_models,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -215,16 +208,12 @@ class CircuitTypeListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(CircuitType)
|
@register_model_view(CircuitType)
|
||||||
class CircuitTypeView(generic.ObjectView):
|
class CircuitTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = CircuitType.objects.all()
|
queryset = CircuitType.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Circuit.objects.restrict(request.user, 'view').filter(type=instance), 'type_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,23 +14,20 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class NestedDataSourceSerializer(WritableNestedSerializer):
|
class NestedDataSourceSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:datasource-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DataSource
|
model = DataSource
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDataFileSerializer(WritableNestedSerializer):
|
class NestedDataFileSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:datafile-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DataFile
|
model = DataFile
|
||||||
fields = ['id', 'url', 'display', 'path']
|
fields = ['id', 'url', 'display_url', 'display', 'path']
|
||||||
|
|
||||||
|
|
||||||
class NestedJobSerializer(serializers.ModelSerializer):
|
class NestedJobSerializer(serializers.ModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
|
|
||||||
status = ChoiceField(choices=JobStatusChoices)
|
status = ChoiceField(choices=JobStatusChoices)
|
||||||
user = UserSerializer(
|
user = UserSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -39,4 +36,4 @@ class NestedJobSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Job
|
model = Job
|
||||||
fields = ['url', 'created', 'completed', 'user', 'status']
|
fields = ['url', 'display_url', 'created', 'completed', 'user', 'status']
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from .serializers_.change_logging import *
|
||||||
from .serializers_.data import *
|
from .serializers_.data import *
|
||||||
from .serializers_.jobs import *
|
from .serializers_.jobs import *
|
||||||
from .nested_serializers import *
|
from .nested_serializers import *
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from drf_spectacular.utils import extend_schema_field
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from extras.choices import *
|
from core.choices import *
|
||||||
from extras.models import ObjectChange
|
from core.models import ObjectChange
|
||||||
from netbox.api.exceptions import SerializerNotFound
|
from netbox.api.exceptions import SerializerNotFound
|
||||||
from netbox.api.fields import ChoiceField, ContentTypeField
|
from netbox.api.fields import ChoiceField, ContentTypeField
|
||||||
from netbox.api.serializers import BaseModelSerializer
|
from netbox.api.serializers import BaseModelSerializer
|
||||||
@ -15,7 +15,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ObjectChangeSerializer(BaseModelSerializer):
|
class ObjectChangeSerializer(BaseModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:objectchange-detail')
|
|
||||||
user = UserSerializer(
|
user = UserSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
read_only=True
|
read_only=True
|
||||||
@ -44,8 +43,8 @@ class ObjectChangeSerializer(BaseModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ObjectChange
|
model = ObjectChange
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type',
|
'id', 'url', 'display_url', 'display', 'time', 'user', 'user_name', 'request_id', 'action',
|
||||||
'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
|
'changed_object_type', 'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
|
||||||
]
|
]
|
||||||
|
|
||||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
@extend_schema_field(serializers.JSONField(allow_null=True))
|
@ -13,9 +13,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class DataSourceSerializer(NetBoxModelSerializer):
|
class DataSourceSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(
|
|
||||||
view_name='core-api:datasource-detail'
|
|
||||||
)
|
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=get_data_backend_choices()
|
choices=get_data_backend_choices()
|
||||||
)
|
)
|
||||||
@ -30,16 +27,13 @@ class DataSourceSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DataSource
|
model = DataSource
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
|
'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
|
||||||
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
|
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DataFileSerializer(NetBoxModelSerializer):
|
class DataFileSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(
|
|
||||||
view_name='core-api:datafile-detail'
|
|
||||||
)
|
|
||||||
source = DataSourceSerializer(
|
source = DataSourceSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
read_only=True
|
read_only=True
|
||||||
@ -48,6 +42,6 @@ class DataFileSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DataFile
|
model = DataFile
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'path')
|
brief_fields = ('id', 'url', 'display', 'path')
|
||||||
|
@ -12,7 +12,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class JobSerializer(BaseModelSerializer):
|
class JobSerializer(BaseModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
|
|
||||||
user = UserSerializer(
|
user = UserSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
read_only=True
|
read_only=True
|
||||||
@ -25,7 +24,7 @@ class JobSerializer(BaseModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Job
|
model = Job
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
|
'id', 'url', 'display_url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
|
||||||
'started', 'completed', 'user', 'data', 'error', 'job_id',
|
'started', 'completed', 'user', 'data', 'error', 'job_id',
|
||||||
]
|
]
|
||||||
brief_fields = ('url', 'created', 'completed', 'user', 'status')
|
brief_fields = ('url', 'created', 'completed', 'user', 'status')
|
||||||
|
@ -5,12 +5,10 @@ from . import views
|
|||||||
router = NetBoxRouter()
|
router = NetBoxRouter()
|
||||||
router.APIRootView = views.CoreRootView
|
router.APIRootView = views.CoreRootView
|
||||||
|
|
||||||
# Data sources
|
|
||||||
router.register('data-sources', views.DataSourceViewSet)
|
router.register('data-sources', views.DataSourceViewSet)
|
||||||
router.register('data-files', views.DataFileViewSet)
|
router.register('data-files', views.DataFileViewSet)
|
||||||
|
|
||||||
# Jobs
|
|
||||||
router.register('jobs', views.JobViewSet)
|
router.register('jobs', views.JobViewSet)
|
||||||
|
router.register('object-changes', views.ObjectChangeViewSet)
|
||||||
|
|
||||||
app_name = 'core-api'
|
app_name = 'core-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -8,6 +8,7 @@ from rest_framework.viewsets import ReadOnlyModelViewSet
|
|||||||
|
|
||||||
from core import filtersets
|
from core import filtersets
|
||||||
from core.models import *
|
from core.models import *
|
||||||
|
from netbox.api.metadata import ContentTypeMetadata
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
@ -54,3 +55,13 @@ class JobViewSet(ReadOnlyModelViewSet):
|
|||||||
queryset = Job.objects.all()
|
queryset = Job.objects.all()
|
||||||
serializer_class = serializers.JobSerializer
|
serializer_class = serializers.JobSerializer
|
||||||
filterset_class = filtersets.JobFilterSet
|
filterset_class = filtersets.JobFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeViewSet(ReadOnlyModelViewSet):
|
||||||
|
"""
|
||||||
|
Retrieve a list of recent changes.
|
||||||
|
"""
|
||||||
|
metadata_class = ContentTypeMetadata
|
||||||
|
queryset = ObjectChange.objects.valid_models()
|
||||||
|
serializer_class = serializers.ObjectChangeSerializer
|
||||||
|
filterset_class = filtersets.ObjectChangeFilterSet
|
||||||
|
@ -64,3 +64,20 @@ class JobStatusChoices(ChoiceSet):
|
|||||||
STATUS_ERRORED,
|
STATUS_ERRORED,
|
||||||
STATUS_FAILED,
|
STATUS_FAILED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# ObjectChanges
|
||||||
|
#
|
||||||
|
|
||||||
|
class ObjectChangeActionChoices(ChoiceSet):
|
||||||
|
|
||||||
|
ACTION_CREATE = 'create'
|
||||||
|
ACTION_UPDATE = 'update'
|
||||||
|
ACTION_DELETE = 'delete'
|
||||||
|
|
||||||
|
CHOICES = (
|
||||||
|
(ACTION_CREATE, _('Created'), 'green'),
|
||||||
|
(ACTION_UPDATE, _('Updated'), 'blue'),
|
||||||
|
(ACTION_DELETE, _('Deleted'), 'red'),
|
||||||
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
@ -5,6 +7,7 @@ import django_filters
|
|||||||
|
|
||||||
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
|
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
|
||||||
from netbox.utils import get_data_backend_choices
|
from netbox.utils import get_data_backend_choices
|
||||||
|
from utilities.filters import ContentTypeFilter
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
@ -13,6 +16,7 @@ __all__ = (
|
|||||||
'DataFileFilterSet',
|
'DataFileFilterSet',
|
||||||
'DataSourceFilterSet',
|
'DataSourceFilterSet',
|
||||||
'JobFilterSet',
|
'JobFilterSet',
|
||||||
|
'ObjectChangeFilterSet',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -126,6 +130,43 @@ class JobFilterSet(BaseFilterSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeFilterSet(BaseFilterSet):
|
||||||
|
q = django_filters.CharFilter(
|
||||||
|
method='search',
|
||||||
|
label=_('Search'),
|
||||||
|
)
|
||||||
|
time = django_filters.DateTimeFromToRangeFilter()
|
||||||
|
changed_object_type = ContentTypeFilter()
|
||||||
|
changed_object_type_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=ContentType.objects.all()
|
||||||
|
)
|
||||||
|
user_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=get_user_model().objects.all(),
|
||||||
|
label=_('User (ID)'),
|
||||||
|
)
|
||||||
|
user = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='user__username',
|
||||||
|
queryset=get_user_model().objects.all(),
|
||||||
|
to_field_name='username',
|
||||||
|
label=_('User name'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ObjectChange
|
||||||
|
fields = (
|
||||||
|
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
|
||||||
|
'related_object_type', 'related_object_id', 'object_repr',
|
||||||
|
)
|
||||||
|
|
||||||
|
def search(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(
|
||||||
|
Q(user_name__icontains=value) |
|
||||||
|
Q(object_repr__icontains=value)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigRevisionFilterSet(BaseFilterSet):
|
class ConfigRevisionFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
|
@ -7,8 +7,10 @@ from core.models import *
|
|||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from netbox.forms.mixins import SavedFiltersMixin
|
from netbox.forms.mixins import SavedFiltersMixin
|
||||||
from netbox.utils import get_data_backend_choices
|
from netbox.utils import get_data_backend_choices
|
||||||
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm
|
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
|
||||||
from utilities.forms.fields import ContentTypeChoiceField, DynamicModelMultipleChoiceField
|
from utilities.forms.fields import (
|
||||||
|
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField,
|
||||||
|
)
|
||||||
from utilities.forms.rendering import FieldSet
|
from utilities.forms.rendering import FieldSet
|
||||||
from utilities.forms.widgets import DateTimePicker
|
from utilities.forms.widgets import DateTimePicker
|
||||||
|
|
||||||
@ -17,6 +19,7 @@ __all__ = (
|
|||||||
'DataFileFilterForm',
|
'DataFileFilterForm',
|
||||||
'DataSourceFilterForm',
|
'DataSourceFilterForm',
|
||||||
'JobFilterForm',
|
'JobFilterForm',
|
||||||
|
'ObjectChangeFilterForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -124,6 +127,40 @@ class JobFilterForm(SavedFiltersMixin, FilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeFilterForm(SavedFiltersMixin, FilterForm):
|
||||||
|
model = ObjectChange
|
||||||
|
fieldsets = (
|
||||||
|
FieldSet('q', 'filter_id'),
|
||||||
|
FieldSet('time_before', 'time_after', name=_('Time')),
|
||||||
|
FieldSet('action', 'user_id', 'changed_object_type_id', name=_('Attributes')),
|
||||||
|
)
|
||||||
|
time_after = forms.DateTimeField(
|
||||||
|
required=False,
|
||||||
|
label=_('After'),
|
||||||
|
widget=DateTimePicker()
|
||||||
|
)
|
||||||
|
time_before = forms.DateTimeField(
|
||||||
|
required=False,
|
||||||
|
label=_('Before'),
|
||||||
|
widget=DateTimePicker()
|
||||||
|
)
|
||||||
|
action = forms.ChoiceField(
|
||||||
|
label=_('Action'),
|
||||||
|
choices=add_blank_choice(ObjectChangeActionChoices),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
user_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=get_user_model().objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('User')
|
||||||
|
)
|
||||||
|
changed_object_type_id = ContentTypeMultipleChoiceField(
|
||||||
|
queryset=ObjectType.objects.with_feature('change_logging'),
|
||||||
|
required=False,
|
||||||
|
label=_('Object Type'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigRevisionFilterForm(SavedFiltersMixin, FilterForm):
|
class ConfigRevisionFilterForm(SavedFiltersMixin, FilterForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('q', 'filter_id'),
|
FieldSet('q', 'filter_id'),
|
||||||
|
@ -6,6 +6,7 @@ from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'DataFileFilter',
|
'DataFileFilter',
|
||||||
'DataSourceFilter',
|
'DataSourceFilter',
|
||||||
|
'ObjectChangeFilter',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -19,3 +20,9 @@ class DataFileFilter(BaseFilterMixin):
|
|||||||
@autotype_decorator(filtersets.DataSourceFilterSet)
|
@autotype_decorator(filtersets.DataSourceFilterSet)
|
||||||
class DataSourceFilter(BaseFilterMixin):
|
class DataSourceFilter(BaseFilterMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_django.filter(models.ObjectChange, lookups=True)
|
||||||
|
@autotype_decorator(filtersets.ObjectChangeFilterSet)
|
||||||
|
class ObjectChangeFilter(BaseFilterMixin):
|
||||||
|
pass
|
||||||
|
24
netbox/core/graphql/mixins.py
Normal file
24
netbox/core/graphql/mixins.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from typing import Annotated, List
|
||||||
|
|
||||||
|
import strawberry
|
||||||
|
import strawberry_django
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
from core.models import ObjectChange
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ChangelogMixin',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry.type
|
||||||
|
class ChangelogMixin:
|
||||||
|
|
||||||
|
@strawberry_django.field
|
||||||
|
def changelog(self, info) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]:
|
||||||
|
content_type = ContentType.objects.get_for_model(self)
|
||||||
|
object_changes = ObjectChange.objects.filter(
|
||||||
|
changed_object_type=content_type,
|
||||||
|
changed_object_id=self.pk
|
||||||
|
)
|
||||||
|
return object_changes.restrict(info.context.request.user, 'view')
|
@ -10,6 +10,7 @@ from .filters import *
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'DataFileType',
|
'DataFileType',
|
||||||
'DataSourceType',
|
'DataSourceType',
|
||||||
|
'ObjectChangeType',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -30,3 +31,12 @@ class DataFileType(BaseObjectType):
|
|||||||
class DataSourceType(NetBoxObjectType):
|
class DataSourceType(NetBoxObjectType):
|
||||||
|
|
||||||
datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
|
datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_django.type(
|
||||||
|
models.ObjectChange,
|
||||||
|
fields='__all__',
|
||||||
|
filters=ObjectChangeFilter
|
||||||
|
)
|
||||||
|
class ObjectChangeType(BaseObjectType):
|
||||||
|
pass
|
||||||
|
45
netbox/core/migrations/0011_move_objectchange.py
Normal file
45
netbox/core/migrations/0011_move_objectchange.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
('core', '0010_gfk_indexes'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.SeparateDatabaseAndState(
|
||||||
|
state_operations=[
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ObjectChange',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
('time', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('user_name', models.CharField(editable=False, max_length=150)),
|
||||||
|
('request_id', models.UUIDField(db_index=True, editable=False)),
|
||||||
|
('action', models.CharField(max_length=50)),
|
||||||
|
('changed_object_id', models.PositiveBigIntegerField()),
|
||||||
|
('related_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
|
||||||
|
('object_repr', models.CharField(editable=False, max_length=200)),
|
||||||
|
('prechange_data', models.JSONField(blank=True, editable=False, null=True)),
|
||||||
|
('postchange_data', models.JSONField(blank=True, editable=False, null=True)),
|
||||||
|
('changed_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
|
||||||
|
('related_object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='changes', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'object change',
|
||||||
|
'verbose_name_plural': 'object changes',
|
||||||
|
'ordering': ['-time'],
|
||||||
|
'indexes': [models.Index(fields=['changed_object_type', 'changed_object_id'], name='core_object_changed_c227ce_idx'), models.Index(fields=['related_object_type', 'related_object_id'], name='core_object_related_3375d6_idx')],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
# Table has been renamed from 'extras' app
|
||||||
|
database_operations=[],
|
||||||
|
),
|
||||||
|
]
|
@ -1,5 +1,6 @@
|
|||||||
from .config import *
|
|
||||||
from .contenttypes import *
|
from .contenttypes import *
|
||||||
|
from .change_logging import *
|
||||||
|
from .config import *
|
||||||
from .data import *
|
from .data import *
|
||||||
from .files import *
|
from .files import *
|
||||||
from .jobs import *
|
from .jobs import *
|
||||||
|
@ -8,11 +8,11 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from mptt.models import MPTTModel
|
from mptt.models import MPTTModel
|
||||||
|
|
||||||
from core.models import ObjectType
|
from core.choices import ObjectChangeActionChoices
|
||||||
from extras.choices import *
|
from core.querysets import ObjectChangeQuerySet
|
||||||
from netbox.models.features import ChangeLoggingMixin
|
from netbox.models.features import ChangeLoggingMixin
|
||||||
from utilities.data import shallow_compare_dict
|
from utilities.data import shallow_compare_dict
|
||||||
from ..querysets import ObjectChangeQuerySet
|
from .contenttypes import ObjectType
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ObjectChange',
|
'ObjectChange',
|
||||||
@ -136,7 +136,7 @@ class ObjectChange(models.Model):
|
|||||||
return super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('extras:objectchange', args=[self.pk])
|
return reverse('core:objectchange', args=[self.pk])
|
||||||
|
|
||||||
def get_action_color(self):
|
def get_action_color(self):
|
||||||
return ObjectChangeActionChoices.colors.get(self.action)
|
return ObjectChangeActionChoices.colors.get(self.action)
|
26
netbox/core/querysets.py
Normal file
26
netbox/core/querysets.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from django.apps import apps
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.utils import ProgrammingError
|
||||||
|
|
||||||
|
from utilities.querysets import RestrictedQuerySet
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ObjectChangeQuerySet',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeQuerySet(RestrictedQuerySet):
|
||||||
|
|
||||||
|
def valid_models(self):
|
||||||
|
# Exclude any change records which refer to an instance of a model that's no longer installed. This
|
||||||
|
# can happen when a plugin is removed but its data remains in the database, for example.
|
||||||
|
try:
|
||||||
|
content_types = ContentType.objects.get_for_models(*apps.get_models()).values()
|
||||||
|
except ProgrammingError:
|
||||||
|
# Handle the case where the database schema has not yet been initialized
|
||||||
|
content_types = ContentType.objects.none()
|
||||||
|
|
||||||
|
content_type_ids = set(
|
||||||
|
ct.pk for ct in content_types
|
||||||
|
)
|
||||||
|
return self.filter(changed_object_type_id__in=content_type_ids)
|
@ -1,3 +1,4 @@
|
|||||||
|
from .change_logging import *
|
||||||
from .config import *
|
from .config import *
|
||||||
from .data import *
|
from .data import *
|
||||||
from .jobs import *
|
from .jobs import *
|
||||||
|
53
netbox/core/tables/change_logging.py
Normal file
53
netbox/core/tables/change_logging.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import django_tables2 as tables
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from core.models import ObjectChange
|
||||||
|
from netbox.tables import NetBoxTable, columns
|
||||||
|
from .template_code import *
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ObjectChangeTable',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeTable(NetBoxTable):
|
||||||
|
time = columns.DateTimeColumn(
|
||||||
|
verbose_name=_('Time'),
|
||||||
|
timespec='minutes',
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
|
user_name = tables.Column(
|
||||||
|
verbose_name=_('Username')
|
||||||
|
)
|
||||||
|
full_name = tables.TemplateColumn(
|
||||||
|
accessor=tables.A('user'),
|
||||||
|
template_code=OBJECTCHANGE_FULL_NAME,
|
||||||
|
verbose_name=_('Full Name'),
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
action = columns.ChoiceFieldColumn(
|
||||||
|
verbose_name=_('Action'),
|
||||||
|
)
|
||||||
|
changed_object_type = columns.ContentTypeColumn(
|
||||||
|
verbose_name=_('Type')
|
||||||
|
)
|
||||||
|
object_repr = tables.TemplateColumn(
|
||||||
|
accessor=tables.A('changed_object'),
|
||||||
|
template_code=OBJECTCHANGE_OBJECT,
|
||||||
|
verbose_name=_('Object'),
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
request_id = tables.TemplateColumn(
|
||||||
|
template_code=OBJECTCHANGE_REQUEST_ID,
|
||||||
|
verbose_name=_('Request ID')
|
||||||
|
)
|
||||||
|
actions = columns.ActionsColumn(
|
||||||
|
actions=()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(NetBoxTable.Meta):
|
||||||
|
model = ObjectChange
|
||||||
|
fields = (
|
||||||
|
'pk', 'id', 'time', 'user_name', 'full_name', 'action', 'changed_object_type', 'object_repr', 'request_id',
|
||||||
|
'actions',
|
||||||
|
)
|
16
netbox/core/tables/template_code.py
Normal file
16
netbox/core/tables/template_code.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
OBJECTCHANGE_FULL_NAME = """
|
||||||
|
{% load helpers %}
|
||||||
|
{{ value.get_full_name|placeholder }}
|
||||||
|
"""
|
||||||
|
|
||||||
|
OBJECTCHANGE_OBJECT = """
|
||||||
|
{% if value and value.get_absolute_url %}
|
||||||
|
<a href="{{ value.get_absolute_url }}">{{ record.object_repr }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ record.object_repr }}
|
||||||
|
{% endif %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
OBJECTCHANGE_REQUEST_ID = """
|
||||||
|
<a href="{% url 'core:objectchange_list' %}?request_id={{ value }}">{{ value }}</a>
|
||||||
|
"""
|
@ -3,11 +3,12 @@ from django.test import override_settings
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from core.models import ObjectType
|
from core.choices import ObjectChangeActionChoices
|
||||||
|
from core.models import ObjectChange, ObjectType
|
||||||
from dcim.choices import SiteStatusChoices
|
from dcim.choices import SiteStatusChoices
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.models import CustomField, CustomFieldChoiceSet, ObjectChange, Tag
|
from extras.models import CustomField, CustomFieldChoiceSet, Tag
|
||||||
from utilities.testing import APITestCase
|
from utilities.testing import APITestCase
|
||||||
from utilities.testing.utils import create_tags, post_data
|
from utilities.testing.utils import create_tags, post_data
|
||||||
from utilities.testing.views import ModelViewTestCase
|
from utilities.testing.views import ModelViewTestCase
|
@ -1,7 +1,13 @@
|
|||||||
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from utilities.testing import ChangeLoggedFilterSetTests
|
|
||||||
|
from dcim.models import Site
|
||||||
|
from ipam.models import IPAddress
|
||||||
|
from users.models import User
|
||||||
|
from utilities.testing import BaseFilterSetTests, ChangeLoggedFilterSetTests
|
||||||
from ..choices import *
|
from ..choices import *
|
||||||
from ..filtersets import *
|
from ..filtersets import *
|
||||||
from ..models import *
|
from ..models import *
|
||||||
@ -132,3 +138,99 @@ class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
'a78168c7c97115bafd96450ed03ea43acec495094c5caa28f0d02e20e3a76cc2',
|
'a78168c7c97115bafd96450ed03ea43acec495094c5caa28f0d02e20e3a76cc2',
|
||||||
]}
|
]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
|
||||||
|
queryset = ObjectChange.objects.all()
|
||||||
|
filterset = ObjectChangeFilterSet
|
||||||
|
ignore_fields = ('prechange_data', 'postchange_data')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
users = (
|
||||||
|
User(username='user1'),
|
||||||
|
User(username='user2'),
|
||||||
|
User(username='user3'),
|
||||||
|
)
|
||||||
|
User.objects.bulk_create(users)
|
||||||
|
|
||||||
|
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||||
|
ipaddress = IPAddress.objects.create(address='192.0.2.1/24')
|
||||||
|
|
||||||
|
object_changes = (
|
||||||
|
ObjectChange(
|
||||||
|
user=users[0],
|
||||||
|
user_name=users[0].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||||
|
changed_object=site,
|
||||||
|
object_repr=str(site),
|
||||||
|
postchange_data={'name': site.name, 'slug': site.slug}
|
||||||
|
),
|
||||||
|
ObjectChange(
|
||||||
|
user=users[0],
|
||||||
|
user_name=users[0].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_UPDATE,
|
||||||
|
changed_object=site,
|
||||||
|
object_repr=str(site),
|
||||||
|
postchange_data={'name': site.name, 'slug': site.slug}
|
||||||
|
),
|
||||||
|
ObjectChange(
|
||||||
|
user=users[1],
|
||||||
|
user_name=users[1].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_DELETE,
|
||||||
|
changed_object=site,
|
||||||
|
object_repr=str(site),
|
||||||
|
postchange_data={'name': site.name, 'slug': site.slug}
|
||||||
|
),
|
||||||
|
ObjectChange(
|
||||||
|
user=users[1],
|
||||||
|
user_name=users[1].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||||
|
changed_object=ipaddress,
|
||||||
|
object_repr=str(ipaddress),
|
||||||
|
postchange_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||||
|
),
|
||||||
|
ObjectChange(
|
||||||
|
user=users[2],
|
||||||
|
user_name=users[2].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_UPDATE,
|
||||||
|
changed_object=ipaddress,
|
||||||
|
object_repr=str(ipaddress),
|
||||||
|
postchange_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||||
|
),
|
||||||
|
ObjectChange(
|
||||||
|
user=users[2],
|
||||||
|
user_name=users[2].username,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=ObjectChangeActionChoices.ACTION_DELETE,
|
||||||
|
changed_object=ipaddress,
|
||||||
|
object_repr=str(ipaddress),
|
||||||
|
postchange_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ObjectChange.objects.bulk_create(object_changes)
|
||||||
|
|
||||||
|
def test_q(self):
|
||||||
|
params = {'q': 'Site 1'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||||
|
|
||||||
|
def test_user(self):
|
||||||
|
params = {'user_id': User.objects.filter(username__in=['user1', 'user2']).values_list('pk', flat=True)}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'user': ['user1', 'user2']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_user_name(self):
|
||||||
|
params = {'user_name': ['user1', 'user2']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_changed_object_type(self):
|
||||||
|
params = {'changed_object_type': 'dcim.site'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||||
|
params = {'changed_object_type_id': [ContentType.objects.get(app_label='dcim', model='site').pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from core.models import DataSource
|
from core.models import DataSource
|
||||||
from extras.choices import ObjectChangeActionChoices
|
from core.choices import ObjectChangeActionChoices
|
||||||
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import logging
|
import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -10,8 +10,11 @@ from django_rq.workers import get_worker
|
|||||||
from rq.job import Job as RQ_Job, JobStatus
|
from rq.job import Job as RQ_Job, JobStatus
|
||||||
from rq.registry import DeferredJobRegistry, FailedJobRegistry, FinishedJobRegistry, StartedJobRegistry
|
from rq.registry import DeferredJobRegistry, FailedJobRegistry, FinishedJobRegistry, StartedJobRegistry
|
||||||
|
|
||||||
|
from core.choices import ObjectChangeActionChoices
|
||||||
|
from core.models import *
|
||||||
|
from dcim.models import Site
|
||||||
|
from users.models import User
|
||||||
from utilities.testing import TestCase, ViewTestCases, create_tags
|
from utilities.testing import TestCase, ViewTestCases, create_tags
|
||||||
from ..models import *
|
|
||||||
|
|
||||||
|
|
||||||
class DataSourceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
class DataSourceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
@ -99,6 +102,43 @@ class DataFileTestCase(
|
|||||||
DataFile.objects.bulk_create(data_files)
|
DataFile.objects.bulk_create(data_files)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Convert to StandardTestCases.Views
|
||||||
|
class ObjectChangeTestCase(TestCase):
|
||||||
|
user_permissions = (
|
||||||
|
'core.view_objectchange',
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
# Create three ObjectChanges
|
||||||
|
user = User.objects.create_user(username='testuser2')
|
||||||
|
for i in range(1, 4):
|
||||||
|
oc = site.to_objectchange(action=ObjectChangeActionChoices.ACTION_UPDATE)
|
||||||
|
oc.user = user
|
||||||
|
oc.request_id = uuid.uuid4()
|
||||||
|
oc.save()
|
||||||
|
|
||||||
|
def test_objectchange_list(self):
|
||||||
|
|
||||||
|
url = reverse('core:objectchange_list')
|
||||||
|
params = {
|
||||||
|
"user": User.objects.first().pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
def test_objectchange(self):
|
||||||
|
|
||||||
|
objectchange = ObjectChange.objects.first()
|
||||||
|
response = self.client.get(objectchange.get_absolute_url())
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
|
||||||
class BackgroundTaskTestCase(TestCase):
|
class BackgroundTaskTestCase(TestCase):
|
||||||
user_permissions = ()
|
user_permissions = ()
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ urlpatterns = (
|
|||||||
path('jobs/<int:pk>/', views.JobView.as_view(), name='job'),
|
path('jobs/<int:pk>/', views.JobView.as_view(), name='job'),
|
||||||
path('jobs/<int:pk>/delete/', views.JobDeleteView.as_view(), name='job_delete'),
|
path('jobs/<int:pk>/delete/', views.JobDeleteView.as_view(), name='job_delete'),
|
||||||
|
|
||||||
|
# Change logging
|
||||||
|
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||||
|
path('changelog/<int:pk>/', include(get_model_urls('core', 'objectchange'))),
|
||||||
|
|
||||||
# Background Tasks
|
# Background Tasks
|
||||||
path('background-queues/', views.BackgroundQueueListView.as_view(), name='background_queue_list'),
|
path('background-queues/', views.BackgroundQueueListView.as_view(), name='background_queue_list'),
|
||||||
path('background-queues/<int:queue_index>/<str:status>/', views.BackgroundTaskListView.as_view(), name='background_task_list'),
|
path('background-queues/<int:queue_index>/<str:status>/', views.BackgroundTaskListView.as_view(), name='background_task_list'),
|
||||||
|
@ -29,10 +29,11 @@ from netbox.config import get_config, PARAMS
|
|||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from netbox.views.generic.base import BaseObjectView
|
from netbox.views.generic.base import BaseObjectView
|
||||||
from netbox.views.generic.mixins import TableMixin
|
from netbox.views.generic.mixins import TableMixin
|
||||||
|
from utilities.data import shallow_compare_dict
|
||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.htmx import htmx_partial
|
from utilities.htmx import htmx_partial
|
||||||
from utilities.query import count_related
|
from utilities.query import count_related
|
||||||
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
|
from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModelsMixin, register_model_view
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
@ -51,16 +52,12 @@ class DataSourceListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(DataSource)
|
@register_model_view(DataSource)
|
||||||
class DataSourceView(generic.ObjectView):
|
class DataSourceView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = DataSource.objects.all()
|
queryset = DataSource.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(DataFile.objects.restrict(request.user, 'view').filter(source=instance), 'source_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -176,6 +173,75 @@ class JobBulkDeleteView(generic.BulkDeleteView):
|
|||||||
table = tables.JobTable
|
table = tables.JobTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Change logging
|
||||||
|
#
|
||||||
|
|
||||||
|
class ObjectChangeListView(generic.ObjectListView):
|
||||||
|
queryset = ObjectChange.objects.valid_models()
|
||||||
|
filterset = filtersets.ObjectChangeFilterSet
|
||||||
|
filterset_form = forms.ObjectChangeFilterForm
|
||||||
|
table = tables.ObjectChangeTable
|
||||||
|
template_name = 'core/objectchange_list.html'
|
||||||
|
actions = {
|
||||||
|
'export': {'view'},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@register_model_view(ObjectChange)
|
||||||
|
class ObjectChangeView(generic.ObjectView):
|
||||||
|
queryset = ObjectChange.objects.valid_models()
|
||||||
|
|
||||||
|
def get_extra_context(self, request, instance):
|
||||||
|
related_changes = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
|
||||||
|
request_id=instance.request_id
|
||||||
|
).exclude(
|
||||||
|
pk=instance.pk
|
||||||
|
)
|
||||||
|
related_changes_table = tables.ObjectChangeTable(
|
||||||
|
data=related_changes[:50],
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
objectchanges = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
|
||||||
|
changed_object_type=instance.changed_object_type,
|
||||||
|
changed_object_id=instance.changed_object_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
next_change = objectchanges.filter(time__gt=instance.time).order_by('time').first()
|
||||||
|
prev_change = objectchanges.filter(time__lt=instance.time).order_by('-time').first()
|
||||||
|
|
||||||
|
if not instance.prechange_data and instance.action in ['update', 'delete'] and prev_change:
|
||||||
|
non_atomic_change = True
|
||||||
|
prechange_data = prev_change.postchange_data_clean
|
||||||
|
else:
|
||||||
|
non_atomic_change = False
|
||||||
|
prechange_data = instance.prechange_data_clean
|
||||||
|
|
||||||
|
if prechange_data and instance.postchange_data:
|
||||||
|
diff_added = shallow_compare_dict(
|
||||||
|
prechange_data or dict(),
|
||||||
|
instance.postchange_data_clean or dict(),
|
||||||
|
exclude=['last_updated'],
|
||||||
|
)
|
||||||
|
diff_removed = {
|
||||||
|
x: prechange_data.get(x) for x in diff_added
|
||||||
|
} if prechange_data else {}
|
||||||
|
else:
|
||||||
|
diff_added = None
|
||||||
|
diff_removed = None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'diff_added': diff_added,
|
||||||
|
'diff_removed': diff_removed,
|
||||||
|
'next_change': next_change,
|
||||||
|
'prev_change': prev_change,
|
||||||
|
'related_changes_table': related_changes_table,
|
||||||
|
'related_changes_count': related_changes.count(),
|
||||||
|
'non_atomic_change': non_atomic_change
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Config Revisions
|
# Config Revisions
|
||||||
#
|
#
|
||||||
|
@ -57,34 +57,31 @@ __all__ = [
|
|||||||
exclude_fields=('site_count',),
|
exclude_fields=('site_count',),
|
||||||
)
|
)
|
||||||
class NestedRegionSerializer(WritableNestedSerializer):
|
class NestedRegionSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
|
||||||
site_count = serializers.IntegerField(read_only=True)
|
site_count = serializers.IntegerField(read_only=True)
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Region
|
model = models.Region
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'site_count', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'site_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('site_count',),
|
exclude_fields=('site_count',),
|
||||||
)
|
)
|
||||||
class NestedSiteGroupSerializer(WritableNestedSerializer):
|
class NestedSiteGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
|
|
||||||
site_count = serializers.IntegerField(read_only=True)
|
site_count = serializers.IntegerField(read_only=True)
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.SiteGroup
|
model = models.SiteGroup
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'site_count', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'site_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
class NestedSiteSerializer(WritableNestedSerializer):
|
class NestedSiteSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Site
|
model = models.Site
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -95,46 +92,42 @@ class NestedSiteSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('rack_count',),
|
exclude_fields=('rack_count',),
|
||||||
)
|
)
|
||||||
class NestedLocationSerializer(WritableNestedSerializer):
|
class NestedLocationSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
|
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = serializers.IntegerField(read_only=True)
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Location
|
model = models.Location
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'rack_count', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('rack_count',),
|
exclude_fields=('rack_count',),
|
||||||
)
|
)
|
||||||
class NestedRackRoleSerializer(WritableNestedSerializer):
|
class NestedRackRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
|
||||||
rack_count = RelatedObjectCountField('racks')
|
rack_count = RelatedObjectCountField('racks')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RackRole
|
model = models.RackRole
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'rack_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('device_count',),
|
exclude_fields=('device_count',),
|
||||||
)
|
)
|
||||||
class NestedRackSerializer(WritableNestedSerializer):
|
class NestedRackSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
|
||||||
device_count = RelatedObjectCountField('devices')
|
device_count = RelatedObjectCountField('devices')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Rack
|
model = models.Rack
|
||||||
fields = ['id', 'url', 'display', 'name', 'device_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'device_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedRackReservationSerializer(WritableNestedSerializer):
|
class NestedRackReservationSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
|
|
||||||
user = serializers.SerializerMethodField(read_only=True)
|
user = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RackReservation
|
model = models.RackReservation
|
||||||
fields = ['id', 'url', 'display', 'user', 'units']
|
fields = ['id', 'url', 'display_url', 'display', 'user', 'units']
|
||||||
|
|
||||||
def get_user(self, obj):
|
def get_user(self, obj):
|
||||||
return obj.user.username
|
return obj.user.username
|
||||||
@ -148,34 +141,31 @@ class NestedRackReservationSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('devicetype_count',),
|
exclude_fields=('devicetype_count',),
|
||||||
)
|
)
|
||||||
class NestedManufacturerSerializer(WritableNestedSerializer):
|
class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
|
||||||
devicetype_count = RelatedObjectCountField('device_types')
|
devicetype_count = RelatedObjectCountField('device_types')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Manufacturer
|
model = models.Manufacturer
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'devicetype_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'devicetype_count']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('device_count',),
|
exclude_fields=('device_count',),
|
||||||
)
|
)
|
||||||
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
|
||||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||||
device_count = RelatedObjectCountField('instances')
|
device_count = RelatedObjectCountField('instances')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceType
|
model = models.DeviceType
|
||||||
fields = ['id', 'url', 'display', 'manufacturer', 'model', 'slug', 'device_count']
|
fields = ['id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'slug', 'device_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedModuleTypeSerializer(WritableNestedSerializer):
|
class NestedModuleTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
|
|
||||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ModuleType
|
model = models.ModuleType
|
||||||
fields = ['id', 'url', 'display', 'manufacturer', 'model']
|
fields = ['id', 'url', 'display_url', 'display', 'manufacturer', 'model']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -183,84 +173,74 @@ class NestedModuleTypeSerializer(WritableNestedSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class NestedConsolePortTemplateSerializer(WritableNestedSerializer):
|
class NestedConsolePortTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConsolePortTemplate
|
model = models.ConsolePortTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedConsoleServerPortTemplateSerializer(WritableNestedSerializer):
|
class NestedConsoleServerPortTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConsoleServerPortTemplate
|
model = models.ConsoleServerPortTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedPowerPortTemplateSerializer(WritableNestedSerializer):
|
class NestedPowerPortTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerPortTemplate
|
model = models.PowerPortTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedPowerOutletTemplateSerializer(WritableNestedSerializer):
|
class NestedPowerOutletTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerOutletTemplate
|
model = models.PowerOutletTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedInterfaceTemplateSerializer(WritableNestedSerializer):
|
class NestedInterfaceTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InterfaceTemplate
|
model = models.InterfaceTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedRearPortTemplateSerializer(WritableNestedSerializer):
|
class NestedRearPortTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RearPortTemplate
|
model = models.RearPortTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
|
class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.FrontPortTemplate
|
model = models.FrontPortTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedModuleBayTemplateSerializer(WritableNestedSerializer):
|
class NestedModuleBayTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ModuleBayTemplate
|
model = models.ModuleBayTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceBayTemplateSerializer(WritableNestedSerializer):
|
class NestedDeviceBayTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceBayTemplate
|
model = models.DeviceBayTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedInventoryItemTemplateSerializer(WritableNestedSerializer):
|
class NestedInventoryItemTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemtemplate-detail')
|
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InventoryItemTemplate
|
model = models.InventoryItemTemplate
|
||||||
fields = ['id', 'url', 'display', 'name', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'name', '_depth']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -271,171 +251,154 @@ class NestedInventoryItemTemplateSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('device_count', 'virtualmachine_count'),
|
exclude_fields=('device_count', 'virtualmachine_count'),
|
||||||
)
|
)
|
||||||
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
|
||||||
device_count = RelatedObjectCountField('devices')
|
device_count = RelatedObjectCountField('devices')
|
||||||
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceRole
|
model = models.DeviceRole
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('device_count', 'virtualmachine_count'),
|
exclude_fields=('device_count', 'virtualmachine_count'),
|
||||||
)
|
)
|
||||||
class NestedPlatformSerializer(WritableNestedSerializer):
|
class NestedPlatformSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
|
||||||
device_count = RelatedObjectCountField('devices')
|
device_count = RelatedObjectCountField('devices')
|
||||||
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Platform
|
model = models.Platform
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceSerializer(WritableNestedSerializer):
|
class NestedDeviceSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Device
|
model = models.Device
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class ModuleNestedModuleBaySerializer(WritableNestedSerializer):
|
class ModuleNestedModuleBaySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ModuleBay
|
model = models.ModuleBay
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayNestedModuleSerializer(WritableNestedSerializer):
|
class ModuleBayNestedModuleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Module
|
model = models.Module
|
||||||
fields = ['id', 'url', 'display', 'serial']
|
fields = ['id', 'url', 'display_url', 'display', 'serial']
|
||||||
|
|
||||||
|
|
||||||
class NestedModuleSerializer(WritableNestedSerializer):
|
class NestedModuleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
module_bay = ModuleNestedModuleBaySerializer(read_only=True)
|
module_bay = ModuleNestedModuleBaySerializer(read_only=True)
|
||||||
module_type = NestedModuleTypeSerializer(read_only=True)
|
module_type = NestedModuleTypeSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Module
|
model = models.Module
|
||||||
fields = ['id', 'url', 'display', 'device', 'module_bay', 'module_type']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type']
|
||||||
|
|
||||||
|
|
||||||
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConsoleServerPort
|
model = models.ConsoleServerPort
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedConsolePortSerializer(WritableNestedSerializer):
|
class NestedConsolePortSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConsolePort
|
model = models.ConsolePort
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerOutlet
|
model = models.PowerOutlet
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedPowerPortSerializer(WritableNestedSerializer):
|
class NestedPowerPortSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerPort
|
model = models.PowerPort
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedInterfaceSerializer(WritableNestedSerializer):
|
class NestedInterfaceSerializer(WritableNestedSerializer):
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Interface
|
model = models.Interface
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedRearPortSerializer(WritableNestedSerializer):
|
class NestedRearPortSerializer(WritableNestedSerializer):
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RearPort
|
model = models.RearPort
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedFrontPortSerializer(WritableNestedSerializer):
|
class NestedFrontPortSerializer(WritableNestedSerializer):
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
|
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.FrontPort
|
model = models.FrontPort
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedModuleBaySerializer(WritableNestedSerializer):
|
class NestedModuleBaySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
|
||||||
installed_module = ModuleBayNestedModuleSerializer(required=False, allow_null=True)
|
installed_module = ModuleBayNestedModuleSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ModuleBay
|
model = models.ModuleBay
|
||||||
fields = ['id', 'url', 'display', 'installed_module', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'installed_module', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceBay
|
model = models.DeviceBay
|
||||||
fields = ['id', 'url', 'display', 'device', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedInventoryItemSerializer(WritableNestedSerializer):
|
class NestedInventoryItemSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
|
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InventoryItem
|
model = models.InventoryItem
|
||||||
fields = ['id', 'url', 'display', 'device', 'name', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'device', 'name', '_depth']
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
exclude_fields=('inventoryitem_count',),
|
exclude_fields=('inventoryitem_count',),
|
||||||
)
|
)
|
||||||
class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
|
class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
|
|
||||||
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InventoryItemRole
|
model = models.InventoryItemRole
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug', 'inventoryitem_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'inventoryitem_count']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -443,11 +406,10 @@ class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class NestedCableSerializer(WritableNestedSerializer):
|
class NestedCableSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Cable
|
model = models.Cable
|
||||||
fields = ['id', 'url', 'display', 'label']
|
fields = ['id', 'url', 'display_url', 'display', 'label']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -458,13 +420,12 @@ class NestedCableSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('member_count',),
|
exclude_fields=('member_count',),
|
||||||
)
|
)
|
||||||
class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
|
||||||
master = NestedDeviceSerializer()
|
master = NestedDeviceSerializer()
|
||||||
member_count = serializers.IntegerField(read_only=True)
|
member_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.VirtualChassis
|
model = models.VirtualChassis
|
||||||
fields = ['id', 'url', 'display', 'name', 'master', 'member_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'master', 'member_count']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -475,27 +436,24 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
|||||||
exclude_fields=('powerfeed_count',),
|
exclude_fields=('powerfeed_count',),
|
||||||
)
|
)
|
||||||
class NestedPowerPanelSerializer(WritableNestedSerializer):
|
class NestedPowerPanelSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
|
|
||||||
powerfeed_count = RelatedObjectCountField('powerfeeds')
|
powerfeed_count = RelatedObjectCountField('powerfeeds')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerPanel
|
model = models.PowerPanel
|
||||||
fields = ['id', 'url', 'display', 'name', 'powerfeed_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'powerfeed_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedPowerFeedSerializer(WritableNestedSerializer):
|
class NestedPowerFeedSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
|
|
||||||
_occupied = serializers.BooleanField(required=False, read_only=True)
|
_occupied = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerFeed
|
model = models.PowerFeed
|
||||||
fields = ['id', 'url', 'display', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
class NestedVirtualDeviceContextSerializer(WritableNestedSerializer):
|
class NestedVirtualDeviceContextSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualdevicecontext-detail')
|
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.VirtualDeviceContext
|
model = models.VirtualDeviceContext
|
||||||
fields = ['id', 'url', 'display', 'name', 'identifier', 'device']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'identifier', 'device']
|
||||||
|
@ -7,7 +7,7 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import Cable, CablePath, CableTermination
|
from dcim.models import Cable, CablePath, CableTermination
|
||||||
from netbox.api.fields import ChoiceField, ContentTypeField
|
from netbox.api.fields import ChoiceField, ContentTypeField
|
||||||
from netbox.api.serializers import GenericObjectSerializer, NetBoxModelSerializer
|
from netbox.api.serializers import BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer
|
||||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||||
from utilities.api import get_serializer_for_model
|
from utilities.api import get_serializer_for_model
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class CableSerializer(NetBoxModelSerializer):
|
class CableSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
|
|
||||||
a_terminations = GenericObjectSerializer(many=True, required=False)
|
a_terminations = GenericObjectSerializer(many=True, required=False)
|
||||||
b_terminations = GenericObjectSerializer(many=True, required=False)
|
b_terminations = GenericObjectSerializer(many=True, required=False)
|
||||||
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
||||||
@ -31,27 +30,26 @@ class CableSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant', 'label', 'color',
|
'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant',
|
||||||
'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'label', 'description')
|
brief_fields = ('id', 'url', 'display', 'label', 'description')
|
||||||
|
|
||||||
|
|
||||||
class TracedCableSerializer(serializers.ModelSerializer):
|
class TracedCableSerializer(BaseModelSerializer):
|
||||||
"""
|
"""
|
||||||
Used only while tracing a cable path.
|
Used only while tracing a cable path.
|
||||||
"""
|
"""
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'description',
|
'id', 'url', 'display_url', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'description',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CableTerminationSerializer(NetBoxModelSerializer):
|
class CableTerminationSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cabletermination-detail')
|
|
||||||
termination_type = ContentTypeField(
|
termination_type = ContentTypeField(
|
||||||
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
|
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
|
||||||
)
|
)
|
||||||
@ -60,8 +58,8 @@ class CableTerminationSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CableTermination
|
model = CableTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id', 'termination',
|
'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id',
|
||||||
'created', 'last_updated',
|
'termination', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||||
|
@ -41,7 +41,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -63,7 +62,7 @@ class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer,
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
'last_updated', '_occupied',
|
'last_updated', '_occupied',
|
||||||
@ -72,7 +71,6 @@ class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer,
|
|||||||
|
|
||||||
|
|
||||||
class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -94,7 +92,7 @@ class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, Conne
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
'last_updated', '_occupied',
|
'last_updated', '_occupied',
|
||||||
@ -103,7 +101,6 @@ class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, Conne
|
|||||||
|
|
||||||
|
|
||||||
class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -121,8 +118,8 @@ class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw',
|
||||||
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
'allocated_draw', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||||
'created', 'last_updated', '_occupied',
|
'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
@ -130,7 +127,6 @@ class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
|
|||||||
|
|
||||||
|
|
||||||
class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -159,8 +155,8 @@ class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, Conne
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port',
|
||||||
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||||
'created', 'last_updated', '_occupied',
|
'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
@ -168,7 +164,6 @@ class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, Conne
|
|||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
vdcs = SerializedPKRelatedField(
|
vdcs = SerializedPKRelatedField(
|
||||||
queryset=VirtualDeviceContext.objects.all(),
|
queryset=VirtualDeviceContext.objects.all(),
|
||||||
@ -224,11 +219,11 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge',
|
'id', 'url', 'display_url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled',
|
||||||
'lag', 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role',
|
'parent', 'bridge', 'lag', 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description',
|
||||||
'rf_channel', 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
'mode', 'rf_role', 'rf_channel', 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width',
|
||||||
'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers',
|
'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'cable_end', 'wireless_link',
|
||||||
'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints',
|
'link_peers', 'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints',
|
||||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
|
'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
|
||||||
]
|
]
|
||||||
@ -250,7 +245,6 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
|
|||||||
|
|
||||||
|
|
||||||
class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -263,9 +257,9 @@ class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions',
|
||||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created',
|
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags',
|
||||||
'last_updated', '_occupied',
|
'custom_fields', 'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
@ -274,15 +268,13 @@ class FrontPortRearPortSerializer(WritableNestedSerializer):
|
|||||||
"""
|
"""
|
||||||
NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
|
NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
|
||||||
"""
|
"""
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = ['id', 'url', 'display', 'name', 'label', 'description']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'label', 'description']
|
||||||
|
|
||||||
|
|
||||||
class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module = ModuleSerializer(
|
module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -296,7 +288,7 @@ class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
|
||||||
'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
||||||
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
@ -304,7 +296,6 @@ class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class ModuleBaySerializer(NetBoxModelSerializer):
|
class ModuleBaySerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
installed_module = ModuleSerializer(
|
installed_module = ModuleSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -316,28 +307,26 @@ class ModuleBaySerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ModuleBay
|
model = ModuleBay
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'name', 'installed_module', 'label', 'position', 'description', 'tags',
|
'id', 'url', 'display_url', 'display', 'device', 'name', 'installed_module', 'label', 'position',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBaySerializer(NetBoxModelSerializer):
|
class DeviceBaySerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'name', 'label', 'description', 'installed_device', 'tags',
|
'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemSerializer(NetBoxModelSerializer):
|
class InventoryItemSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||||
role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
|
role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
|
||||||
@ -353,9 +342,9 @@ class InventoryItemSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
|
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer',
|
||||||
'asset_tag', 'discovered', 'description', 'component_type', 'component_id', 'component', 'tags',
|
'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type', 'component_id',
|
||||||
'custom_fields', 'created', 'last_updated', '_depth',
|
'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class DeviceSerializer(NetBoxModelSerializer):
|
class DeviceSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
|
||||||
device_type = DeviceTypeSerializer(nested=True)
|
device_type = DeviceTypeSerializer(nested=True)
|
||||||
role = DeviceRoleSerializer(nested=True)
|
role = DeviceRoleSerializer(nested=True)
|
||||||
tenant = TenantSerializer(
|
tenant = TenantSerializer(
|
||||||
@ -78,13 +77,13 @@ class DeviceSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial', 'asset_tag', 'site',
|
'id', 'url', 'display_url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial',
|
||||||
'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device', 'status', 'airflow',
|
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
|
||||||
'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis', 'vc_position',
|
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
|
||||||
'vc_priority', 'description', 'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields',
|
'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'local_context_data', 'tags',
|
||||||
'created', 'last_updated', 'console_port_count', 'console_server_port_count', 'power_port_count',
|
'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
|
||||||
'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count', 'device_bay_count',
|
'power_port_count', 'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count',
|
||||||
'module_bay_count', 'inventory_item_count',
|
'device_bay_count', 'module_bay_count', 'inventory_item_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
@ -105,13 +104,13 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
|
|||||||
|
|
||||||
class Meta(DeviceSerializer.Meta):
|
class Meta(DeviceSerializer.Meta):
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial', 'asset_tag', 'site',
|
'id', 'url', 'display_url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial',
|
||||||
'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device', 'status', 'airflow',
|
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
|
||||||
'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis', 'vc_position',
|
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
|
||||||
'vc_priority', 'description', 'comments', 'config_template', 'config_context', 'local_context_data', 'tags',
|
'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'config_context',
|
||||||
'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
|
'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated', 'console_port_count',
|
||||||
'power_port_count', 'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count',
|
'console_server_port_count', 'power_port_count', 'power_outlet_count', 'interface_count',
|
||||||
'device_bay_count', 'module_bay_count', 'inventory_item_count',
|
'front_port_count', 'rear_port_count', 'device_bay_count', 'module_bay_count', 'inventory_item_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||||
@ -120,7 +119,6 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class VirtualDeviceContextSerializer(NetBoxModelSerializer):
|
class VirtualDeviceContextSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualdevicecontext-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
identifier = serializers.IntegerField(allow_null=True, max_value=32767, min_value=0, required=False, default=None)
|
identifier = serializers.IntegerField(allow_null=True, max_value=32767, min_value=0, required=False, default=None)
|
||||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
@ -135,15 +133,14 @@ class VirtualDeviceContextSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualDeviceContext
|
model = VirtualDeviceContext
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'device', 'identifier', 'tenant', 'primary_ip', 'primary_ip4',
|
'id', 'url', 'display_url', 'display', 'name', 'device', 'identifier', 'tenant', 'primary_ip',
|
||||||
'primary_ip6', 'status', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'primary_ip4', 'primary_ip6', 'status', 'description', 'comments', 'tags', 'custom_fields',
|
||||||
'interface_count',
|
'created', 'last_updated', 'interface_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'identifier', 'device', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'identifier', 'device', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ModuleSerializer(NetBoxModelSerializer):
|
class ModuleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
|
||||||
device = DeviceSerializer(nested=True)
|
device = DeviceSerializer(nested=True)
|
||||||
module_bay = NestedModuleBaySerializer()
|
module_bay = NestedModuleBaySerializer()
|
||||||
module_type = ModuleTypeSerializer(nested=True)
|
module_type = ModuleTypeSerializer(nested=True)
|
||||||
@ -152,7 +149,7 @@ class ModuleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Module
|
model = Module
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag',
|
'id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial',
|
||||||
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'asset_tag', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')
|
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')
|
||||||
|
@ -32,7 +32,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -54,14 +53,13 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConsolePortTemplate
|
model = ConsolePortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
|
||||||
'last_updated',
|
'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -83,14 +81,13 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConsoleServerPortTemplate
|
model = ConsoleServerPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
|
||||||
'last_updated',
|
'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -113,14 +110,13 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPortTemplate
|
model = PowerPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
|
||||||
'allocated_draw', 'description', 'created', 'last_updated',
|
'maximum_draw', 'allocated_draw', 'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -154,14 +150,13 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerOutletTemplate
|
model = PowerOutletTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
|
||||||
'description', 'created', 'last_updated',
|
'power_port', 'feed_leg', 'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -201,14 +196,13 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceTemplate
|
model = InterfaceTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled',
|
||||||
'description', 'bridge', 'poe_mode', 'poe_type', 'rf_role', 'created', 'last_updated',
|
'mgmt_only', 'description', 'bridge', 'poe_mode', 'poe_type', 'rf_role', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateSerializer(ValidatedModelSerializer):
|
class RearPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
required=False,
|
required=False,
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -226,14 +220,13 @@ class RearPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color',
|
||||||
'description', 'created', 'last_updated',
|
'positions', 'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False,
|
required=False,
|
||||||
@ -252,14 +245,13 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'rear_port',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color',
|
||||||
'rear_port_position', 'description', 'created', 'last_updated',
|
'rear_port', 'rear_port_position', 'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayTemplateSerializer(ValidatedModelSerializer):
|
class ModuleBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True
|
nested=True
|
||||||
)
|
)
|
||||||
@ -267,26 +259,27 @@ class ModuleBayTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ModuleBayTemplate
|
model = ModuleBayTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'name', 'label', 'position', 'description', 'created',
|
'id', 'url', 'display', 'device_type', 'name', 'label', 'position', 'description',
|
||||||
'last_updated',
|
'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True
|
nested=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceBayTemplate
|
model = DeviceBayTemplate
|
||||||
fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated']
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device_type', 'name', 'label', 'description',
|
||||||
|
'created', 'last_updated'
|
||||||
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemTemplateSerializer(ValidatedModelSerializer):
|
class InventoryItemTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemtemplate-detail')
|
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
nested=True
|
nested=True
|
||||||
)
|
)
|
||||||
@ -313,8 +306,9 @@ class InventoryItemTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItemTemplate
|
model = InventoryItemTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id',
|
'id', 'url', 'display', 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer',
|
||||||
'description', 'component_type', 'component_id', 'component', 'created', 'last_updated', '_depth',
|
'part_id', 'description', 'component_type', 'component_id', 'component', 'created', 'last_updated',
|
||||||
|
'_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', '_depth')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', '_depth')
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class DeviceTypeSerializer(NetBoxModelSerializer):
|
class DeviceTypeSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
|
||||||
manufacturer = ManufacturerSerializer(nested=True)
|
manufacturer = ManufacturerSerializer(nested=True)
|
||||||
default_platform = PlatformSerializer(nested=True, required=False, allow_null=True)
|
default_platform = PlatformSerializer(nested=True, required=False, allow_null=True)
|
||||||
u_height = serializers.DecimalField(
|
u_height = serializers.DecimalField(
|
||||||
@ -51,26 +50,25 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height',
|
'id', 'url', 'display_url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number',
|
||||||
'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit',
|
'u_height', 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
|
||||||
'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields',
|
||||||
'device_count', 'console_port_template_count', 'console_server_port_template_count',
|
'created', 'last_updated', 'device_count', 'console_port_template_count',
|
||||||
'power_port_template_count', 'power_outlet_template_count', 'interface_template_count',
|
'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count',
|
||||||
'front_port_template_count', 'rear_port_template_count', 'device_bay_template_count',
|
'interface_template_count', 'front_port_template_count', 'rear_port_template_count',
|
||||||
'module_bay_template_count', 'inventory_item_template_count',
|
'device_bay_template_count', 'module_bay_template_count', 'inventory_item_template_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count')
|
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count')
|
||||||
|
|
||||||
|
|
||||||
class ModuleTypeSerializer(NetBoxModelSerializer):
|
class ModuleTypeSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
|
|
||||||
manufacturer = ManufacturerSerializer(nested=True)
|
manufacturer = ManufacturerSerializer(nested=True)
|
||||||
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
|
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ModuleType
|
model = ModuleType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'description',
|
'id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'part_number', 'weight', 'weight_unit',
|
||||||
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'description')
|
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'description')
|
||||||
|
@ -10,7 +10,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ManufacturerSerializer(NetBoxModelSerializer):
|
class ManufacturerSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
devicetype_count = RelatedObjectCountField('device_types')
|
devicetype_count = RelatedObjectCountField('device_types')
|
||||||
@ -20,7 +19,7 @@ class ManufacturerSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
|
||||||
'devicetype_count', 'inventoryitem_count', 'platform_count',
|
'created', 'last_updated', 'devicetype_count', 'inventoryitem_count', 'platform_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')
|
||||||
|
@ -12,7 +12,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class PlatformSerializer(NetBoxModelSerializer):
|
class PlatformSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
|
||||||
manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True)
|
manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True)
|
||||||
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ class PlatformSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'manufacturer', 'config_template', 'description',
|
||||||
'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
|
||||||
|
@ -17,7 +17,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class PowerPanelSerializer(NetBoxModelSerializer):
|
class PowerPanelSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
|
|
||||||
site = SiteSerializer(nested=True)
|
site = SiteSerializer(nested=True)
|
||||||
location = LocationSerializer(
|
location = LocationSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -32,14 +31,13 @@ class PowerPanelSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags', 'custom_fields',
|
'id', 'url', 'display_url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags',
|
||||||
'powerfeed_count', 'created', 'last_updated',
|
'custom_fields', 'powerfeed_count', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'powerfeed_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', 'powerfeed_count')
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
|
|
||||||
power_panel = PowerPanelSerializer(nested=True)
|
power_panel = PowerPanelSerializer(nested=True)
|
||||||
rack = RackSerializer(
|
rack = RackSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
@ -72,9 +70,9 @@ class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PowerFeed
|
model = PowerFeed
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
|
'id', 'url', 'display_url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply',
|
||||||
'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
'phase', 'voltage', 'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
||||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'description',
|
'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
|
||||||
'tenant', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
'description', 'tenant', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'cable', '_occupied')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', 'cable', '_occupied')
|
||||||
|
@ -20,7 +20,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class RackRoleSerializer(NetBoxModelSerializer):
|
class RackRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
rack_count = RelatedObjectCountField('racks')
|
rack_count = RelatedObjectCountField('racks')
|
||||||
@ -28,14 +27,13 @@ class RackRoleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RackRole
|
model = RackRole
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
|
||||||
'last_updated', 'rack_count',
|
'created', 'last_updated', 'rack_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
|
||||||
|
|
||||||
|
|
||||||
class RackSerializer(NetBoxModelSerializer):
|
class RackSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
|
||||||
site = SiteSerializer(nested=True)
|
site = SiteSerializer(nested=True)
|
||||||
location = LocationSerializer(nested=True, required=False, allow_null=True, default=None)
|
location = LocationSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||||
@ -55,16 +53,15 @@ class RackSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
|
'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status',
|
||||||
'asset_tag', 'type', 'width', 'u_height', 'starting_unit', 'weight', 'max_weight', 'weight_unit',
|
'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'starting_unit', 'weight', 'max_weight',
|
||||||
'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'description', 'comments',
|
'weight_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'description',
|
||||||
'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
|
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
|
||||||
|
|
||||||
|
|
||||||
class RackReservationSerializer(NetBoxModelSerializer):
|
class RackReservationSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
|
|
||||||
rack = RackSerializer(nested=True)
|
rack = RackSerializer(nested=True)
|
||||||
user = UserSerializer(nested=True)
|
user = UserSerializer(nested=True)
|
||||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||||
@ -72,8 +69,8 @@ class RackReservationSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description',
|
'id', 'url', 'display_url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant',
|
||||||
'comments', 'tags', 'custom_fields',
|
'description', 'comments', 'tags', 'custom_fields',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'user', 'description', 'units')
|
brief_fields = ('id', 'url', 'display', 'user', 'description', 'units')
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class DeviceRoleSerializer(NetBoxModelSerializer):
|
class DeviceRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
|
||||||
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
@ -22,14 +21,13 @@ class DeviceRoleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template',
|
||||||
'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
|
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
||||||
@ -37,7 +35,7 @@ class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItemRole
|
model = InventoryItemRole
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
|
||||||
'last_updated', 'inventoryitem_count',
|
'created', 'last_updated', 'inventoryitem_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'inventoryitem_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'inventoryitem_count')
|
||||||
|
@ -19,35 +19,32 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class RegionSerializer(NestedGroupModelSerializer):
|
class RegionSerializer(NestedGroupModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
|
||||||
parent = NestedRegionSerializer(required=False, allow_null=True, default=None)
|
parent = NestedRegionSerializer(required=False, allow_null=True, default=None)
|
||||||
site_count = serializers.IntegerField(read_only=True, default=0)
|
site_count = serializers.IntegerField(read_only=True, default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Region
|
model = Region
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
|
||||||
'last_updated', 'site_count', '_depth',
|
'created', 'last_updated', 'site_count', '_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupSerializer(NestedGroupModelSerializer):
|
class SiteGroupSerializer(NestedGroupModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
|
|
||||||
parent = NestedSiteGroupSerializer(required=False, allow_null=True, default=None)
|
parent = NestedSiteGroupSerializer(required=False, allow_null=True, default=None)
|
||||||
site_count = serializers.IntegerField(read_only=True, default=0)
|
site_count = serializers.IntegerField(read_only=True, default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SiteGroup
|
model = SiteGroup
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
|
||||||
'last_updated', 'site_count', '_depth',
|
'created', 'last_updated', 'site_count', '_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
|
||||||
|
|
||||||
|
|
||||||
class SiteSerializer(NetBoxModelSerializer):
|
class SiteSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
|
||||||
status = ChoiceField(choices=SiteStatusChoices, required=False)
|
status = ChoiceField(choices=SiteStatusChoices, required=False)
|
||||||
region = RegionSerializer(nested=True, required=False, allow_null=True)
|
region = RegionSerializer(nested=True, required=False, allow_null=True)
|
||||||
group = SiteGroupSerializer(nested=True, required=False, allow_null=True)
|
group = SiteGroupSerializer(nested=True, required=False, allow_null=True)
|
||||||
@ -72,16 +69,15 @@ class SiteSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Site
|
model = Site
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'time_zone',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility',
|
||||||
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'asns', 'tags',
|
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude',
|
||||||
'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count', 'prefix_count', 'rack_count',
|
'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count',
|
||||||
'virtualmachine_count', 'vlan_count',
|
'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')
|
||||||
|
|
||||||
|
|
||||||
class LocationSerializer(NestedGroupModelSerializer):
|
class LocationSerializer(NestedGroupModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
|
|
||||||
site = SiteSerializer(nested=True)
|
site = SiteSerializer(nested=True)
|
||||||
parent = NestedLocationSerializer(required=False, allow_null=True, default=None)
|
parent = NestedLocationSerializer(required=False, allow_null=True, default=None)
|
||||||
status = ChoiceField(choices=LocationStatusChoices, required=False)
|
status = ChoiceField(choices=LocationStatusChoices, required=False)
|
||||||
@ -92,7 +88,7 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility', 'description',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
|
||||||
'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
|
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')
|
||||||
|
@ -10,7 +10,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class VirtualChassisSerializer(NetBoxModelSerializer):
|
class VirtualChassisSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
|
||||||
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
|
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
|
||||||
members = NestedDeviceSerializer(many=True, read_only=True)
|
members = NestedDeviceSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ class VirtualChassisSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualChassis
|
model = VirtualChassis
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags', 'custom_fields',
|
'id', 'url', 'display_url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags',
|
||||||
'created', 'last_updated', 'member_count', 'members',
|
'custom_fields', 'created', 'last_updated', 'member_count', 'members',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'master', 'description', 'member_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'master', 'description', 'member_count')
|
||||||
|
@ -219,9 +219,9 @@ class RackViewSet(NetBoxModelViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Enable filtering rack units by ID
|
# Enable filtering rack units by ID
|
||||||
q = data['q']
|
if q := data['q']:
|
||||||
if q:
|
q = q.lower()
|
||||||
elevation = [u for u in elevation if q in str(u['id']) or q in str(u['name'])]
|
elevation = [u for u in elevation if q in str(u['id']) or q in str(u['name']).lower()]
|
||||||
|
|
||||||
page = self.paginate_queryset(elevation)
|
page = self.paginate_queryset(elevation)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
@ -465,7 +465,10 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
|
|||||||
label=_('Cluster'),
|
label=_('Cluster'),
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
selector=True
|
selector=True,
|
||||||
|
query_params={
|
||||||
|
'site_id': ['$site', 'null']
|
||||||
|
},
|
||||||
)
|
)
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
local_context_data = JSONField(
|
local_context_data = JSONField(
|
||||||
@ -656,11 +659,6 @@ class CableForm(TenancyForm, NetBoxModelForm):
|
|||||||
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
|
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
|
||||||
'length', 'length_unit', 'description', 'comments', 'tags',
|
'length', 'length_unit', 'description', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
error_messages = {
|
|
||||||
'length': {
|
|
||||||
'max_value': _('Maximum length is 32767 (any unit)')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelForm(NetBoxModelForm):
|
class PowerPanelForm(NetBoxModelForm):
|
||||||
|
@ -3,14 +3,10 @@ from typing import Annotated, List, Union
|
|||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
|
|
||||||
|
from core.graphql.mixins import ChangelogMixin
|
||||||
from dcim import models
|
from dcim import models
|
||||||
from extras.graphql.mixins import (
|
from extras.graphql.mixins import (
|
||||||
ChangelogMixin,
|
ConfigContextMixin, ContactsMixin, CustomFieldsMixin, ImageAttachmentsMixin, TagsMixin,
|
||||||
ConfigContextMixin,
|
|
||||||
ContactsMixin,
|
|
||||||
CustomFieldsMixin,
|
|
||||||
ImageAttachmentsMixin,
|
|
||||||
TagsMixin,
|
|
||||||
)
|
)
|
||||||
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
|
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
|
||||||
from netbox.graphql.scalars import BigInt
|
from netbox.graphql.scalars import BigInt
|
||||||
|
@ -393,6 +393,8 @@ class CableTraceSVG:
|
|||||||
labels = [f"{cable}"] if len(links) > 2 else [f"Wireless {cable}", cable.get_status_display()]
|
labels = [f"{cable}"] if len(links) > 2 else [f"Wireless {cable}", cable.get_status_display()]
|
||||||
if cable.ssid:
|
if cable.ssid:
|
||||||
description.append(f"{cable.ssid}")
|
description.append(f"{cable.ssid}")
|
||||||
|
if cable.distance and cable.distance_unit:
|
||||||
|
description.append(f"{cable.distance} {cable.get_distance_unit_display()}")
|
||||||
near = [term for term in near_terminations if term.object == cable.interface_a]
|
near = [term for term in near_terminations if term.object == cable.interface_a]
|
||||||
far = [term for term in far_terminations if term.object == cable.interface_b]
|
far = [term for term in far_terminations if term.object == cable.interface_b]
|
||||||
if not (near and far):
|
if not (near and far):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from dcim.models import Cable
|
from dcim.models import Cable
|
||||||
@ -35,7 +36,7 @@ class CableTerminationsColumn(tables.Column):
|
|||||||
|
|
||||||
def render(self, value):
|
def render(self, value):
|
||||||
links = [
|
links = [
|
||||||
f'<a href="{term.get_absolute_url()}">{term}</a>' for term in self._get_terminations(value)
|
f'<a href="{term.get_absolute_url()}">{escape(term)}</a>' for term in self._get_terminations(value)
|
||||||
]
|
]
|
||||||
return mark_safe('<br />'.join(links) or '—')
|
return mark_safe('<br />'.join(links) or '—')
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ class CableTable(TenancyColumnsMixin, NetBoxTable):
|
|||||||
status = columns.ChoiceFieldColumn()
|
status = columns.ChoiceFieldColumn()
|
||||||
length = columns.TemplateColumn(
|
length = columns.TemplateColumn(
|
||||||
template_code=CABLE_LENGTH,
|
template_code=CABLE_LENGTH,
|
||||||
order_by=('_abs_length', 'length_unit')
|
order_by=('_abs_length')
|
||||||
)
|
)
|
||||||
color = columns.ColorColumn()
|
color = columns.ColorColumn()
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
@ -8,6 +8,7 @@ from dcim.models import *
|
|||||||
from extras.models import CustomField
|
from extras.models import CustomField
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.data import drange
|
from utilities.data import drange
|
||||||
|
from virtualization.models import Cluster, ClusterType
|
||||||
|
|
||||||
|
|
||||||
class LocationTestCase(TestCase):
|
class LocationTestCase(TestCase):
|
||||||
@ -533,6 +534,36 @@ class DeviceTestCase(TestCase):
|
|||||||
device2.full_clean()
|
device2.full_clean()
|
||||||
device2.save()
|
device2.save()
|
||||||
|
|
||||||
|
def test_device_mismatched_site_cluster(self):
|
||||||
|
cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
|
||||||
|
Cluster.objects.create(name='Cluster 1', type=cluster_type)
|
||||||
|
|
||||||
|
sites = (
|
||||||
|
Site(name='Site 1', slug='site-1'),
|
||||||
|
Site(name='Site 2', slug='site-2'),
|
||||||
|
)
|
||||||
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
|
clusters = (
|
||||||
|
Cluster(name='Cluster 1', type=cluster_type, site=sites[0]),
|
||||||
|
Cluster(name='Cluster 2', type=cluster_type, site=sites[1]),
|
||||||
|
Cluster(name='Cluster 3', type=cluster_type, site=None),
|
||||||
|
)
|
||||||
|
Cluster.objects.bulk_create(clusters)
|
||||||
|
|
||||||
|
device_type = DeviceType.objects.first()
|
||||||
|
device_role = DeviceRole.objects.first()
|
||||||
|
|
||||||
|
# Device with site only should pass
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role).full_clean()
|
||||||
|
|
||||||
|
# Device with site, cluster non-site should pass
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[2]).full_clean()
|
||||||
|
|
||||||
|
# Device with mismatched site & cluster should fail
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[1]).full_clean()
|
||||||
|
|
||||||
|
|
||||||
class CableTestCase(TestCase):
|
class CableTestCase(TestCase):
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from jinja2.exceptions import TemplateError
|
|||||||
|
|
||||||
from circuits.models import Circuit, CircuitTermination
|
from circuits.models import Circuit, CircuitTermination
|
||||||
from extras.views import ObjectConfigContextView
|
from extras.views import ObjectConfigContextView
|
||||||
from ipam.models import ASN, IPAddress, Prefix, VLAN, VLANGroup
|
from ipam.models import ASN, IPAddress, VLANGroup
|
||||||
from ipam.tables import InterfaceVLANTable
|
from ipam.tables import InterfaceVLANTable
|
||||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
@ -27,7 +27,9 @@ from utilities.paginator import EnhancedPaginator, get_paginate_count
|
|||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.query import count_related
|
from utilities.query import count_related
|
||||||
from utilities.query_functions import CollateAsChar
|
from utilities.query_functions import CollateAsChar
|
||||||
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
|
from utilities.views import (
|
||||||
|
GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
|
||||||
|
)
|
||||||
from virtualization.filtersets import VirtualMachineFilterSet
|
from virtualization.filtersets import VirtualMachineFilterSet
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
from virtualization.tables import VirtualMachineTable
|
from virtualization.tables import VirtualMachineTable
|
||||||
@ -226,19 +228,21 @@ class RegionListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Region)
|
@register_model_view(Region)
|
||||||
class RegionView(generic.ObjectView):
|
class RegionView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Region.objects.all()
|
queryset = Region.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
regions = instance.get_descendants(include_self=True)
|
regions = instance.get_descendants(include_self=True)
|
||||||
related_models = (
|
|
||||||
(Site.objects.restrict(request.user, 'view').filter(region__in=regions), 'region_id'),
|
|
||||||
(Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
||||||
(Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(
|
||||||
|
request,
|
||||||
|
regions,
|
||||||
|
extra=(
|
||||||
|
(Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
||||||
|
(Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -306,19 +310,21 @@ class SiteGroupListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(SiteGroup)
|
@register_model_view(SiteGroup)
|
||||||
class SiteGroupView(generic.ObjectView):
|
class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = SiteGroup.objects.all()
|
queryset = SiteGroup.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
groups = instance.get_descendants(include_self=True)
|
groups = instance.get_descendants(include_self=True)
|
||||||
related_models = (
|
|
||||||
(Site.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
|
|
||||||
(Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
||||||
(Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(
|
||||||
|
request,
|
||||||
|
groups,
|
||||||
|
extra=(
|
||||||
|
(Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
||||||
|
(Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -380,31 +386,25 @@ class SiteListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Site)
|
@register_model_view(Site)
|
||||||
class SiteView(generic.ObjectView):
|
class SiteView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Site.objects.prefetch_related('tenant__group')
|
queryset = Site.objects.prefetch_related('tenant__group')
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
return {
|
||||||
# DCIM
|
'related_models': self.get_related_models(
|
||||||
(Location.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
request,
|
||||||
(Rack.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
instance,
|
||||||
(Device.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
[CableTermination, CircuitTermination],
|
||||||
# Virtualization
|
(
|
||||||
(VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance), 'site_id'),
|
|
||||||
# IPAM
|
|
||||||
(Prefix.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
||||||
(ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
|
|
||||||
(VLANGroup.objects.restrict(request.user, 'view').filter(
|
(VLANGroup.objects.restrict(request.user, 'view').filter(
|
||||||
scope_type=ContentType.objects.get_for_model(Site),
|
scope_type=ContentType.objects.get_for_model(Site),
|
||||||
scope_id=instance.pk
|
scope_id=instance.pk
|
||||||
), 'site'),
|
), 'site'),
|
||||||
(VLAN.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
(ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
|
||||||
# Circuits
|
(Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(),
|
||||||
(Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'),
|
'site_id'),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
return {
|
|
||||||
'related_models': related_models,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -466,18 +466,13 @@ class LocationListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Location)
|
@register_model_view(Location)
|
||||||
class LocationView(generic.ObjectView):
|
class LocationView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Location.objects.all()
|
queryset = Location.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
locations = instance.get_descendants(include_self=True)
|
locations = instance.get_descendants(include_self=True)
|
||||||
related_models = (
|
|
||||||
(Rack.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
|
|
||||||
(Device.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, locations, [CableTermination]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -541,16 +536,12 @@ class RackRoleListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(RackRole)
|
@register_model_view(RackRole)
|
||||||
class RackRoleView(generic.ObjectView):
|
class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = RackRole.objects.all()
|
queryset = RackRole.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Rack.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -655,15 +646,10 @@ class RackElevationListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Rack)
|
@register_model_view(Rack)
|
||||||
class RackView(generic.ObjectView):
|
class RackView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
|
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Device.objects.restrict(request.user, 'view').filter(rack=instance), 'rack_id'),
|
|
||||||
(PowerFeed.objects.restrict(request.user).filter(rack=instance), 'rack_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
|
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
|
||||||
|
|
||||||
if instance.location:
|
if instance.location:
|
||||||
@ -679,7 +665,7 @@ class RackView(generic.ObjectView):
|
|||||||
])
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance, [CableTermination]),
|
||||||
'next_rack': next_rack,
|
'next_rack': next_rack,
|
||||||
'prev_rack': prev_rack,
|
'prev_rack': prev_rack,
|
||||||
'svg_extra': svg_extra,
|
'svg_extra': svg_extra,
|
||||||
@ -838,19 +824,12 @@ class ManufacturerListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Manufacturer)
|
@register_model_view(Manufacturer)
|
||||||
class ManufacturerView(generic.ObjectView):
|
class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Manufacturer.objects.all()
|
queryset = Manufacturer.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(DeviceType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
||||||
(ModuleType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
||||||
(InventoryItem.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
||||||
(Platform.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -912,16 +891,16 @@ class DeviceTypeListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(DeviceType)
|
@register_model_view(DeviceType)
|
||||||
class DeviceTypeView(generic.ObjectView):
|
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = DeviceType.objects.all()
|
queryset = DeviceType.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Device.objects.restrict(request.user).filter(device_type=instance), 'device_type_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance, omit=[
|
||||||
|
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
|
||||||
|
InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
|
||||||
|
RearPortTemplate,
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1151,16 +1130,16 @@ class ModuleTypeListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(ModuleType)
|
@register_model_view(ModuleType)
|
||||||
class ModuleTypeView(generic.ObjectView):
|
class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = ModuleType.objects.all()
|
queryset = ModuleType.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Module.objects.restrict(request.user).filter(module_type=instance), 'module_type_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance, omit=[
|
||||||
|
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
|
||||||
|
InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
|
||||||
|
RearPortTemplate,
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1711,17 +1690,12 @@ class DeviceRoleListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(DeviceRole)
|
@register_model_view(DeviceRole)
|
||||||
class DeviceRoleView(generic.ObjectView):
|
class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = DeviceRole.objects.all()
|
queryset = DeviceRole.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Device.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
||||||
(VirtualMachine.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1775,17 +1749,12 @@ class PlatformListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Platform)
|
@register_model_view(Platform)
|
||||||
class PlatformView(generic.ObjectView):
|
class PlatformView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Platform.objects.all()
|
queryset = Platform.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Device.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
|
|
||||||
(VirtualMachine.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2157,22 +2126,12 @@ class ModuleListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(Module)
|
@register_model_view(Module)
|
||||||
class ModuleView(generic.ObjectView):
|
class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = Module.objects.all()
|
queryset = Module.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Interface.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(ConsolePort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(ConsoleServerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(PowerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(PowerOutlet.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(FrontPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
(RearPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3451,8 +3410,9 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix
|
|||||||
if membership_form.is_valid():
|
if membership_form.is_valid():
|
||||||
|
|
||||||
membership_form.save()
|
membership_form.save()
|
||||||
msg = f'Added member <a href="{device.get_absolute_url()}">{escape(device)}</a>'
|
messages.success(request, mark_safe(
|
||||||
messages.success(request, mark_safe(msg))
|
f'Added member <a href="{device.get_absolute_url()}">{escape(device)}</a>'
|
||||||
|
))
|
||||||
|
|
||||||
if '_addanother' in request.POST:
|
if '_addanother' in request.POST:
|
||||||
return redirect(request.get_full_path())
|
return redirect(request.get_full_path())
|
||||||
@ -3552,16 +3512,12 @@ class PowerPanelListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(PowerPanel)
|
@register_model_view(PowerPanel)
|
||||||
class PowerPanelView(generic.ObjectView):
|
class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = PowerPanel.objects.all()
|
queryset = PowerPanel.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(PowerFeed.objects.restrict(request.user).filter(power_panel=instance), 'power_panel_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(request, instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3665,16 +3621,18 @@ class VirtualDeviceContextListView(generic.ObjectListView):
|
|||||||
|
|
||||||
|
|
||||||
@register_model_view(VirtualDeviceContext)
|
@register_model_view(VirtualDeviceContext)
|
||||||
class VirtualDeviceContextView(generic.ObjectView):
|
class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
|
||||||
queryset = VirtualDeviceContext.objects.all()
|
queryset = VirtualDeviceContext.objects.all()
|
||||||
|
|
||||||
def get_extra_context(self, request, instance):
|
def get_extra_context(self, request, instance):
|
||||||
related_models = (
|
|
||||||
(Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'related_models': related_models,
|
'related_models': self.get_related_models(
|
||||||
|
request,
|
||||||
|
instance,
|
||||||
|
extra=(
|
||||||
|
(Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ from rest_framework.serializers import ValidationError
|
|||||||
|
|
||||||
from core.models import ObjectType
|
from core.models import ObjectType
|
||||||
from extras.choices import CustomFieldTypeChoices
|
from extras.choices import CustomFieldTypeChoices
|
||||||
|
from extras.constants import CUSTOMFIELD_EMPTY_VALUES
|
||||||
from extras.models import CustomField
|
from extras.models import CustomField
|
||||||
from utilities.api import get_serializer_for_model
|
from utilities.api import get_serializer_for_model
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ class CustomFieldsDataField(Field):
|
|||||||
|
|
||||||
# Serialize object and multi-object values
|
# Serialize object and multi-object values
|
||||||
for cf in self._get_custom_fields():
|
for cf in self._get_custom_fields():
|
||||||
if cf.name in data and data[cf.name] not in (None, []) and cf.type in (
|
if cf.name in data and data[cf.name] not in CUSTOMFIELD_EMPTY_VALUES and cf.type in (
|
||||||
CustomFieldTypeChoices.TYPE_OBJECT,
|
CustomFieldTypeChoices.TYPE_OBJECT,
|
||||||
CustomFieldTypeChoices.TYPE_MULTIOBJECT
|
CustomFieldTypeChoices.TYPE_MULTIOBJECT
|
||||||
):
|
):
|
||||||
|
@ -22,79 +22,69 @@ __all__ = [
|
|||||||
|
|
||||||
|
|
||||||
class NestedEventRuleSerializer(WritableNestedSerializer):
|
class NestedEventRuleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:eventrule-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.EventRule
|
model = models.EventRule
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedWebhookSerializer(WritableNestedSerializer):
|
class NestedWebhookSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:webhook-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Webhook
|
model = models.Webhook
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedCustomFieldSerializer(WritableNestedSerializer):
|
class NestedCustomFieldSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.CustomField
|
model = models.CustomField
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedCustomFieldChoiceSetSerializer(WritableNestedSerializer):
|
class NestedCustomFieldChoiceSetSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.CustomFieldChoiceSet
|
model = models.CustomFieldChoiceSet
|
||||||
fields = ['id', 'url', 'display', 'name', 'choices_count']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'choices_count']
|
||||||
|
|
||||||
|
|
||||||
class NestedCustomLinkSerializer(WritableNestedSerializer):
|
class NestedCustomLinkSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.CustomLink
|
model = models.CustomLink
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedConfigContextSerializer(WritableNestedSerializer):
|
class NestedConfigContextSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConfigContext
|
model = models.ConfigContext
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedConfigTemplateSerializer(WritableNestedSerializer):
|
class NestedConfigTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configtemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ConfigTemplate
|
model = models.ConfigTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedExportTemplateSerializer(WritableNestedSerializer):
|
class NestedExportTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ExportTemplate
|
model = models.ExportTemplate
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedSavedFilterSerializer(WritableNestedSerializer):
|
class NestedSavedFilterSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:savedfilter-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.SavedFilter
|
model = models.SavedFilter
|
||||||
fields = ['id', 'url', 'display', 'name', 'slug']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedBookmarkSerializer(WritableNestedSerializer):
|
class NestedBookmarkSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:bookmark-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Bookmark
|
model = models.Bookmark
|
||||||
@ -102,7 +92,6 @@ class NestedBookmarkSerializer(WritableNestedSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class NestedImageAttachmentSerializer(WritableNestedSerializer):
|
class NestedImageAttachmentSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:imageattachment-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ImageAttachment
|
model = models.ImageAttachment
|
||||||
@ -110,11 +99,10 @@ class NestedImageAttachmentSerializer(WritableNestedSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class NestedJournalEntrySerializer(WritableNestedSerializer):
|
class NestedJournalEntrySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:journalentry-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.JournalEntry
|
model = models.JournalEntry
|
||||||
fields = ['id', 'url', 'display', 'created']
|
fields = ['id', 'url', 'display_url', 'display', 'created']
|
||||||
|
|
||||||
|
|
||||||
class NestedScriptSerializer(WritableNestedSerializer):
|
class NestedScriptSerializer(WritableNestedSerializer):
|
||||||
@ -123,12 +111,17 @@ class NestedScriptSerializer(WritableNestedSerializer):
|
|||||||
lookup_field='full_name',
|
lookup_field='full_name',
|
||||||
lookup_url_kwarg='pk'
|
lookup_url_kwarg='pk'
|
||||||
)
|
)
|
||||||
|
display_url = serializers.HyperlinkedIdentityField(
|
||||||
|
view_name='extras:script',
|
||||||
|
lookup_field='full_name',
|
||||||
|
lookup_url_kwarg='pk'
|
||||||
|
)
|
||||||
name = serializers.CharField(read_only=True)
|
name = serializers.CharField(read_only=True)
|
||||||
display = serializers.SerializerMethodField(read_only=True)
|
display = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Script
|
model = models.Script
|
||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
def get_display(self, obj):
|
def get_display(self, obj):
|
||||||
return f'{obj.name} ({obj.module})'
|
return f'{obj.name} ({obj.module})'
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from .serializers_.objecttypes import *
|
from .serializers_.objecttypes import *
|
||||||
from .serializers_.attachments import *
|
from .serializers_.attachments import *
|
||||||
from .serializers_.bookmarks import *
|
from .serializers_.bookmarks import *
|
||||||
from .serializers_.change_logging import *
|
|
||||||
from .serializers_.customfields import *
|
from .serializers_.customfields import *
|
||||||
from .serializers_.customlinks import *
|
from .serializers_.customlinks import *
|
||||||
from .serializers_.dashboard import *
|
from .serializers_.dashboard import *
|
||||||
|
@ -14,7 +14,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ImageAttachmentSerializer(ValidatedModelSerializer):
|
class ImageAttachmentSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:imageattachment-detail')
|
|
||||||
object_type = ContentTypeField(
|
object_type = ContentTypeField(
|
||||||
queryset=ObjectType.objects.all()
|
queryset=ObjectType.objects.all()
|
||||||
)
|
)
|
||||||
@ -23,8 +22,8 @@ class ImageAttachmentSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ImageAttachment
|
model = ImageAttachment
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_type', 'object_id', 'parent', 'name', 'image', 'image_height',
|
'id', 'url', 'display', 'object_type', 'object_id', 'parent', 'name', 'image',
|
||||||
'image_width', 'created', 'last_updated',
|
'image_height', 'image_width', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'image')
|
brief_fields = ('id', 'url', 'display', 'name', 'image')
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class BookmarkSerializer(ValidatedModelSerializer):
|
class BookmarkSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:bookmark-detail')
|
|
||||||
object_type = ContentTypeField(
|
object_type = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('bookmarks'),
|
queryset=ObjectType.objects.with_feature('bookmarks'),
|
||||||
)
|
)
|
||||||
|
@ -20,7 +20,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ConfigContextSerializer(ValidatedModelSerializer):
|
class ConfigContextSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail')
|
|
||||||
regions = SerializedPKRelatedField(
|
regions = SerializedPKRelatedField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
serializer=RegionSerializer,
|
serializer=RegionSerializer,
|
||||||
@ -123,9 +122,9 @@ class ConfigContextSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites',
|
'id', 'url', 'display_url', 'display', 'name', 'weight', 'description', 'is_active', 'regions',
|
||||||
'locations', 'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
|
'site_groups', 'sites', 'locations', 'device_types', 'roles', 'platforms', 'cluster_types',
|
||||||
'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path', 'data_file', 'data_synced', 'data',
|
'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path',
|
||||||
'created', 'last_updated',
|
'data_file', 'data_synced', 'data', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
@ -11,7 +11,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
|
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configtemplate-detail')
|
|
||||||
data_source = DataSourceSerializer(
|
data_source = DataSourceSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False
|
required=False
|
||||||
@ -24,7 +23,7 @@ class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConfigTemplate
|
model = ConfigTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'description', 'environment_params', 'template_code', 'data_source',
|
'id', 'url', 'display_url', 'display', 'name', 'description', 'environment_params', 'template_code',
|
||||||
'data_path', 'data_file', 'data_synced', 'tags', 'created', 'last_updated',
|
'data_source', 'data_path', 'data_file', 'data_synced', 'tags', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
@ -16,7 +16,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
|
|
||||||
base_choices = ChoiceField(
|
base_choices = ChoiceField(
|
||||||
choices=CustomFieldChoiceSetBaseChoices,
|
choices=CustomFieldChoiceSetBaseChoices,
|
||||||
required=False
|
required=False
|
||||||
@ -31,14 +30,13 @@ class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CustomFieldChoiceSet
|
model = CustomFieldChoiceSet
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
|
'id', 'url', 'display_url', 'display', 'name', 'description', 'base_choices', 'extra_choices',
|
||||||
'choices_count', 'created', 'last_updated',
|
'order_alphabetically', 'choices_count', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldSerializer(ValidatedModelSerializer):
|
class CustomFieldSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('custom_fields'),
|
queryset=ObjectType.objects.with_feature('custom_fields'),
|
||||||
many=True
|
many=True
|
||||||
@ -62,10 +60,10 @@ class CustomFieldSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CustomField
|
model = CustomField
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_types', 'type', 'related_object_type', 'data_type', 'name', 'label',
|
'id', 'url', 'display_url', 'display', 'object_types', 'type', 'related_object_type', 'data_type',
|
||||||
'group_name', 'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable',
|
'name', 'label', 'group_name', 'description', 'required', 'search_weight', 'filter_logic', 'ui_visible',
|
||||||
'is_cloneable', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
|
'ui_editable', 'is_cloneable', 'default', 'weight', 'validation_minimum', 'validation_maximum',
|
||||||
'choice_set', 'comments', 'created', 'last_updated',
|
'validation_regex', 'validation_unique', 'choice_set', 'comments', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class CustomLinkSerializer(ValidatedModelSerializer):
|
class CustomLinkSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('custom_links'),
|
queryset=ObjectType.objects.with_feature('custom_links'),
|
||||||
many=True
|
many=True
|
||||||
@ -20,7 +19,7 @@ class CustomLinkSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = CustomLink
|
model = CustomLink
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
|
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'enabled', 'link_text', 'link_url',
|
||||||
'button_class', 'new_window', 'created', 'last_updated',
|
'weight', 'group_name', 'button_class', 'new_window', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name')
|
brief_fields = ('id', 'url', 'display', 'name')
|
||||||
|
@ -21,7 +21,6 @@ __all__ = (
|
|||||||
#
|
#
|
||||||
|
|
||||||
class EventRuleSerializer(NetBoxModelSerializer):
|
class EventRuleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:eventrule-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('event_rules'),
|
queryset=ObjectType.objects.with_feature('event_rules'),
|
||||||
many=True
|
many=True
|
||||||
@ -35,7 +34,7 @@ class EventRuleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = EventRule
|
model = EventRule
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_types', 'name', 'type_create', 'type_update', 'type_delete',
|
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'type_create', 'type_update', 'type_delete',
|
||||||
'type_job_start', 'type_job_end', 'enabled', 'conditions', 'action_type', 'action_object_type',
|
'type_job_start', 'type_job_end', 'enabled', 'conditions', 'action_type', 'action_object_type',
|
||||||
'action_object_id', 'action_object', 'description', 'custom_fields', 'tags', 'created', 'last_updated',
|
'action_object_id', 'action_object', 'description', 'custom_fields', 'tags', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
@ -58,13 +57,12 @@ class EventRuleSerializer(NetBoxModelSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class WebhookSerializer(NetBoxModelSerializer):
|
class WebhookSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:webhook-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Webhook
|
model = Webhook
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'description', 'payload_url', 'http_method', 'http_content_type',
|
'id', 'url', 'display_url', 'display', 'name', 'description', 'payload_url', 'http_method',
|
||||||
'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path', 'custom_fields',
|
'http_content_type', 'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path',
|
||||||
'tags', 'created', 'last_updated',
|
'custom_fields', 'tags', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
@ -12,7 +12,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ExportTemplateSerializer(ValidatedModelSerializer):
|
class ExportTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('export_templates'),
|
queryset=ObjectType.objects.with_feature('export_templates'),
|
||||||
many=True
|
many=True
|
||||||
@ -29,7 +28,7 @@ class ExportTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_types', 'name', 'description', 'template_code', 'mime_type',
|
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'description', 'template_code', 'mime_type',
|
||||||
'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', 'data_synced', 'created',
|
'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', 'data_synced', 'created',
|
||||||
'last_updated',
|
'last_updated',
|
||||||
]
|
]
|
||||||
|
@ -16,7 +16,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class JournalEntrySerializer(NetBoxModelSerializer):
|
class JournalEntrySerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:journalentry-detail')
|
|
||||||
assigned_object_type = ContentTypeField(
|
assigned_object_type = ContentTypeField(
|
||||||
queryset=ObjectType.objects.all()
|
queryset=ObjectType.objects.all()
|
||||||
)
|
)
|
||||||
@ -35,8 +34,8 @@ class JournalEntrySerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = JournalEntry
|
model = JournalEntry
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created',
|
'id', 'url', 'display_url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object',
|
||||||
'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated',
|
'created', 'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'created')
|
brief_fields = ('id', 'url', 'display', 'created')
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class SavedFilterSerializer(ValidatedModelSerializer):
|
class SavedFilterSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:savedfilter-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.all(),
|
queryset=ObjectType.objects.all(),
|
||||||
many=True
|
many=True
|
||||||
@ -20,7 +19,7 @@ class SavedFilterSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = SavedFilter
|
model = SavedFilter
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'object_types', 'name', 'slug', 'description', 'user', 'weight', 'enabled',
|
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'slug', 'description', 'user', 'weight',
|
||||||
'shared', 'parameters', 'created', 'last_updated',
|
'enabled', 'shared', 'parameters', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
|
||||||
|
@ -14,7 +14,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ScriptSerializer(ValidatedModelSerializer):
|
class ScriptSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:script-detail')
|
|
||||||
description = serializers.SerializerMethodField(read_only=True)
|
description = serializers.SerializerMethodField(read_only=True)
|
||||||
vars = serializers.SerializerMethodField(read_only=True)
|
vars = serializers.SerializerMethodField(read_only=True)
|
||||||
result = JobSerializer(nested=True, read_only=True)
|
result = JobSerializer(nested=True, read_only=True)
|
||||||
@ -22,7 +21,7 @@ class ScriptSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Script
|
model = Script
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'module', 'name', 'description', 'vars', 'result', 'display', 'is_executable',
|
'id', 'url', 'display_url', 'module', 'name', 'description', 'vars', 'result', 'display', 'is_executable',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class TagSerializer(ValidatedModelSerializer):
|
class TagSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:tag-detail')
|
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
queryset=ObjectType.objects.with_feature('tags'),
|
queryset=ObjectType.objects.with_feature('tags'),
|
||||||
many=True,
|
many=True,
|
||||||
@ -24,7 +23,7 @@ class TagSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'object_types', 'tagged_items', 'created',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'object_types',
|
||||||
'last_updated',
|
'tagged_items', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')
|
||||||
|
@ -21,7 +21,6 @@ router.register('journal-entries', views.JournalEntryViewSet)
|
|||||||
router.register('config-contexts', views.ConfigContextViewSet)
|
router.register('config-contexts', views.ConfigContextViewSet)
|
||||||
router.register('config-templates', views.ConfigTemplateViewSet)
|
router.register('config-templates', views.ConfigTemplateViewSet)
|
||||||
router.register('scripts', views.ScriptViewSet, basename='script')
|
router.register('scripts', views.ScriptViewSet, basename='script')
|
||||||
router.register('object-changes', views.ObjectChangeViewSet)
|
|
||||||
router.register('object-types', views.ObjectTypeViewSet)
|
router.register('object-types', views.ObjectTypeViewSet)
|
||||||
|
|
||||||
app_name = 'extras-api'
|
app_name = 'extras-api'
|
||||||
|
@ -271,20 +271,6 @@ class ScriptViewSet(ModelViewSet):
|
|||||||
return Response(input_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(input_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Change logging
|
|
||||||
#
|
|
||||||
|
|
||||||
class ObjectChangeViewSet(ReadOnlyModelViewSet):
|
|
||||||
"""
|
|
||||||
Retrieve a list of recent changes.
|
|
||||||
"""
|
|
||||||
metadata_class = ContentTypeMetadata
|
|
||||||
queryset = ObjectChange.objects.valid_models()
|
|
||||||
serializer_class = serializers.ObjectChangeSerializer
|
|
||||||
filterset_class = filtersets.ObjectChangeFilterSet
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Object types
|
# Object types
|
||||||
#
|
#
|
||||||
|
@ -117,27 +117,14 @@ class BookmarkOrderingChoices(ChoiceSet):
|
|||||||
|
|
||||||
ORDERING_NEWEST = '-created'
|
ORDERING_NEWEST = '-created'
|
||||||
ORDERING_OLDEST = 'created'
|
ORDERING_OLDEST = 'created'
|
||||||
|
ORDERING_ALPHABETICAL_AZ = 'name'
|
||||||
|
ORDERING_ALPHABETICAL_ZA = '-name'
|
||||||
|
|
||||||
CHOICES = (
|
CHOICES = (
|
||||||
(ORDERING_NEWEST, _('Newest')),
|
(ORDERING_NEWEST, _('Newest')),
|
||||||
(ORDERING_OLDEST, _('Oldest')),
|
(ORDERING_OLDEST, _('Oldest')),
|
||||||
)
|
(ORDERING_ALPHABETICAL_AZ, _('Alphabetical (A-Z)')),
|
||||||
|
(ORDERING_ALPHABETICAL_ZA, _('Alphabetical (Z-A)')),
|
||||||
#
|
|
||||||
# ObjectChanges
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeActionChoices(ChoiceSet):
|
|
||||||
|
|
||||||
ACTION_CREATE = 'create'
|
|
||||||
ACTION_UPDATE = 'update'
|
|
||||||
ACTION_DELETE = 'delete'
|
|
||||||
|
|
||||||
CHOICES = (
|
|
||||||
(ACTION_CREATE, _('Created'), 'green'),
|
|
||||||
(ACTION_UPDATE, _('Updated'), 'blue'),
|
|
||||||
(ACTION_DELETE, _('Deleted'), 'red'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,23 +135,23 @@ class ConditionSet:
|
|||||||
def __init__(self, ruleset):
|
def __init__(self, ruleset):
|
||||||
if type(ruleset) is not dict:
|
if type(ruleset) is not dict:
|
||||||
raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
|
raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
|
||||||
if len(ruleset) != 1:
|
|
||||||
raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
|
|
||||||
ruleset=len(ruleset)))
|
|
||||||
|
|
||||||
# Determine the logic type
|
if len(ruleset) == 1:
|
||||||
logic = list(ruleset.keys())[0]
|
self.logic = (list(ruleset.keys())[0]).lower()
|
||||||
if type(logic) is not str or logic.lower() not in (AND, OR):
|
if self.logic not in (AND, OR):
|
||||||
raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format(
|
raise ValueError(_("Invalid logic type: must be 'AND' or 'OR'. Please check documentation."))
|
||||||
logic=logic, op_and=AND, op_or=OR
|
|
||||||
))
|
|
||||||
self.logic = logic.lower()
|
|
||||||
|
|
||||||
# Compile the set of Conditions
|
# Compile the set of Conditions
|
||||||
self.conditions = [
|
self.conditions = [
|
||||||
ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
|
ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
|
||||||
for rule in ruleset[self.logic]
|
for rule in ruleset[self.logic]
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.logic = None
|
||||||
|
self.conditions = [Condition(**ruleset)]
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError(_("Incorrect key(s) informed. Please check documentation."))
|
||||||
|
|
||||||
def eval(self, data):
|
def eval(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from extras.choices import LogLevelChoices
|
||||||
|
|
||||||
# Events
|
# Events
|
||||||
EVENT_CREATE = 'create'
|
EVENT_CREATE = 'create'
|
||||||
EVENT_UPDATE = 'update'
|
EVENT_UPDATE = 'update'
|
||||||
@ -5,6 +7,8 @@ EVENT_DELETE = 'delete'
|
|||||||
EVENT_JOB_START = 'job_start'
|
EVENT_JOB_START = 'job_start'
|
||||||
EVENT_JOB_END = 'job_end'
|
EVENT_JOB_END = 'job_end'
|
||||||
|
|
||||||
|
# Custom fields
|
||||||
|
CUSTOMFIELD_EMPTY_VALUES = (None, '', [])
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
HTTP_CONTENT_TYPE_JSON = 'application/json'
|
HTTP_CONTENT_TYPE_JSON = 'application/json'
|
||||||
@ -128,8 +132,17 @@ DEFAULT_DASHBOARD = [
|
|||||||
'title': 'Change Log',
|
'title': 'Change Log',
|
||||||
'color': 'blue',
|
'color': 'blue',
|
||||||
'config': {
|
'config': {
|
||||||
'model': 'extras.objectchange',
|
'model': 'core.objectchange',
|
||||||
'page_size': 25,
|
'page_size': 25,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
LOG_LEVEL_RANK = {
|
||||||
|
LogLevelChoices.LOG_DEFAULT: 0,
|
||||||
|
LogLevelChoices.LOG_DEBUG: 1,
|
||||||
|
LogLevelChoices.LOG_SUCCESS: 2,
|
||||||
|
LogLevelChoices.LOG_INFO: 3,
|
||||||
|
LogLevelChoices.LOG_WARNING: 4,
|
||||||
|
LogLevelChoices.LOG_FAILURE: 5,
|
||||||
|
}
|
||||||
|
@ -381,11 +381,17 @@ class BookmarksWidget(DashboardWidget):
|
|||||||
if request.user.is_anonymous:
|
if request.user.is_anonymous:
|
||||||
bookmarks = list()
|
bookmarks = list()
|
||||||
else:
|
else:
|
||||||
bookmarks = Bookmark.objects.filter(user=request.user).order_by(self.config['order_by'])
|
user_bookmarks = Bookmark.objects.filter(user=request.user)
|
||||||
|
if self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_AZ:
|
||||||
|
bookmarks = sorted(user_bookmarks, key=lambda bookmark: bookmark.__str__().lower())
|
||||||
|
elif self.config['order_by'] == BookmarkOrderingChoices.ORDERING_ALPHABETICAL_ZA:
|
||||||
|
bookmarks = sorted(user_bookmarks, key=lambda bookmark: bookmark.__str__().lower(), reverse=True)
|
||||||
|
else:
|
||||||
|
bookmarks = user_bookmarks.order_by(self.config['order_by'])
|
||||||
if object_types := self.config.get('object_types'):
|
if object_types := self.config.get('object_types'):
|
||||||
models = get_models_from_content_types(object_types)
|
models = get_models_from_content_types(object_types)
|
||||||
conent_types = ObjectType.objects.get_for_models(*models).values()
|
content_types = ObjectType.objects.get_for_models(*models).values()
|
||||||
bookmarks = bookmarks.filter(object_type__in=conent_types)
|
bookmarks = bookmarks.filter(object_type__in=content_types)
|
||||||
if max_items := self.config.get('max_items'):
|
if max_items := self.config.get('max_items'):
|
||||||
bookmarks = bookmarks[:max_items]
|
bookmarks = bookmarks[:max_items]
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
@ -6,6 +8,7 @@ from django.utils.module_loading import import_string
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django_rq import get_queue
|
from django_rq import get_queue
|
||||||
|
|
||||||
|
from core.choices import ObjectChangeActionChoices
|
||||||
from core.models import Job
|
from core.models import Job
|
||||||
from netbox.config import get_config
|
from netbox.config import get_config
|
||||||
from netbox.constants import RQ_QUEUE_DEFAULT
|
from netbox.constants import RQ_QUEUE_DEFAULT
|
||||||
@ -13,7 +16,7 @@ from netbox.registry import registry
|
|||||||
from utilities.api import get_serializer_for_model
|
from utilities.api import get_serializer_for_model
|
||||||
from utilities.rqworker import get_rq_retry
|
from utilities.rqworker import get_rq_retry
|
||||||
from utilities.serialization import serialize_object
|
from utilities.serialization import serialize_object
|
||||||
from .choices import *
|
from .choices import EventRuleActionChoices
|
||||||
from .models import EventRule
|
from .models import EventRule
|
||||||
|
|
||||||
logger = logging.getLogger('netbox.events_processor')
|
logger = logging.getLogger('netbox.events_processor')
|
||||||
|
@ -26,7 +26,6 @@ __all__ = (
|
|||||||
'ImageAttachmentFilterSet',
|
'ImageAttachmentFilterSet',
|
||||||
'JournalEntryFilterSet',
|
'JournalEntryFilterSet',
|
||||||
'LocalConfigContextFilterSet',
|
'LocalConfigContextFilterSet',
|
||||||
'ObjectChangeFilterSet',
|
|
||||||
'ObjectTypeFilterSet',
|
'ObjectTypeFilterSet',
|
||||||
'SavedFilterFilterSet',
|
'SavedFilterFilterSet',
|
||||||
'ScriptFilterSet',
|
'ScriptFilterSet',
|
||||||
@ -155,7 +154,7 @@ class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
|
|||||||
fields = (
|
fields = (
|
||||||
'id', 'name', 'label', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
|
'id', 'name', 'label', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
|
||||||
'ui_editable', 'weight', 'is_cloneable', 'description', 'validation_minimum', 'validation_maximum',
|
'ui_editable', 'weight', 'is_cloneable', 'description', 'validation_minimum', 'validation_maximum',
|
||||||
'validation_regex',
|
'validation_regex', 'validation_unique',
|
||||||
)
|
)
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
@ -645,43 +644,6 @@ class LocalConfigContextFilterSet(django_filters.FilterSet):
|
|||||||
return queryset.exclude(local_context_data__isnull=value)
|
return queryset.exclude(local_context_data__isnull=value)
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeFilterSet(BaseFilterSet):
|
|
||||||
q = django_filters.CharFilter(
|
|
||||||
method='search',
|
|
||||||
label=_('Search'),
|
|
||||||
)
|
|
||||||
time = django_filters.DateTimeFromToRangeFilter()
|
|
||||||
changed_object_type = ContentTypeFilter()
|
|
||||||
changed_object_type_id = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
queryset=ContentType.objects.all()
|
|
||||||
)
|
|
||||||
user_id = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
queryset=get_user_model().objects.all(),
|
|
||||||
label=_('User (ID)'),
|
|
||||||
)
|
|
||||||
user = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
field_name='user__username',
|
|
||||||
queryset=get_user_model().objects.all(),
|
|
||||||
to_field_name='username',
|
|
||||||
label=_('User name'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ObjectChange
|
|
||||||
fields = (
|
|
||||||
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
|
|
||||||
'related_object_type', 'related_object_id', 'object_repr',
|
|
||||||
)
|
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
|
||||||
if not value.strip():
|
|
||||||
return queryset
|
|
||||||
return queryset.filter(
|
|
||||||
Q(user_name__icontains=value) |
|
|
||||||
Q(object_repr__icontains=value)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# ContentTypes
|
# ContentTypes
|
||||||
#
|
#
|
||||||
|
@ -6,6 +6,7 @@ from extras.models import *
|
|||||||
from netbox.forms import NetBoxModelBulkEditForm
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from utilities.forms import BulkEditForm, add_blank_choice
|
from utilities.forms import BulkEditForm, add_blank_choice
|
||||||
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField
|
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField
|
||||||
|
from utilities.forms.rendering import FieldSet
|
||||||
from utilities.forms.widgets import BulkEditNullBooleanSelect
|
from utilities.forms.widgets import BulkEditNullBooleanSelect
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -64,8 +65,32 @@ class CustomFieldBulkEditForm(BulkEditForm):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=BulkEditNullBooleanSelect()
|
widget=BulkEditNullBooleanSelect()
|
||||||
)
|
)
|
||||||
|
validation_minimum = forms.IntegerField(
|
||||||
|
label=_('Minimum value'),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
validation_maximum = forms.IntegerField(
|
||||||
|
label=_('Maximum value'),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
validation_regex = forms.CharField(
|
||||||
|
label=_('Validation regex'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
validation_unique = forms.NullBooleanField(
|
||||||
|
label=_('Must be unique'),
|
||||||
|
required=False,
|
||||||
|
widget=BulkEditNullBooleanSelect()
|
||||||
|
)
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
FieldSet('group_name', 'description', 'weight', 'choice_set', name=_('Attributes')),
|
||||||
|
FieldSet('ui_visible', 'ui_editable', 'is_cloneable', name=_('Behavior')),
|
||||||
|
FieldSet(
|
||||||
|
'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation')
|
||||||
|
),
|
||||||
|
)
|
||||||
nullable_fields = ('group_name', 'description', 'choice_set')
|
nullable_fields = ('group_name', 'description', 'choice_set')
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +71,8 @@ class CustomFieldImportForm(CSVModelForm):
|
|||||||
fields = (
|
fields = (
|
||||||
'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'description',
|
'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'description',
|
||||||
'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
|
'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
|
||||||
'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'comments',
|
'validation_maximum', 'validation_regex', 'validation_unique', 'ui_visible', 'ui_editable', 'is_cloneable',
|
||||||
|
'comments',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from utilities.forms.fields import (
|
|||||||
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
|
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
|
||||||
)
|
)
|
||||||
from utilities.forms.rendering import FieldSet
|
from utilities.forms.rendering import FieldSet
|
||||||
from utilities.forms.widgets import APISelectMultiple, DateTimePicker
|
from utilities.forms.widgets import DateTimePicker
|
||||||
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -28,7 +28,6 @@ __all__ = (
|
|||||||
'ImageAttachmentFilterForm',
|
'ImageAttachmentFilterForm',
|
||||||
'JournalEntryFilterForm',
|
'JournalEntryFilterForm',
|
||||||
'LocalConfigContextFilterForm',
|
'LocalConfigContextFilterForm',
|
||||||
'ObjectChangeFilterForm',
|
|
||||||
'SavedFilterFilterForm',
|
'SavedFilterFilterForm',
|
||||||
'TagFilterForm',
|
'TagFilterForm',
|
||||||
'WebhookFilterForm',
|
'WebhookFilterForm',
|
||||||
@ -42,6 +41,9 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
|
|||||||
'type', 'related_object_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible',
|
'type', 'related_object_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible',
|
||||||
'ui_editable', 'is_cloneable', name=_('Attributes')
|
'ui_editable', 'is_cloneable', name=_('Attributes')
|
||||||
),
|
),
|
||||||
|
FieldSet(
|
||||||
|
'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation')
|
||||||
|
),
|
||||||
)
|
)
|
||||||
related_object_type_id = ContentTypeMultipleChoiceField(
|
related_object_type_id = ContentTypeMultipleChoiceField(
|
||||||
queryset=ObjectType.objects.with_feature('custom_fields'),
|
queryset=ObjectType.objects.with_feature('custom_fields'),
|
||||||
@ -90,6 +92,25 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
|
|||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
validation_minimum = forms.IntegerField(
|
||||||
|
label=_('Minimum value'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
validation_maximum = forms.IntegerField(
|
||||||
|
label=_('Maximum value'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
validation_regex = forms.CharField(
|
||||||
|
label=_('Validation regex'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
validation_unique = forms.NullBooleanField(
|
||||||
|
label=_('Must be unique'),
|
||||||
|
required=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
|
class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
|
||||||
@ -475,37 +496,3 @@ class JournalEntryFilterForm(NetBoxModelFilterSetForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeFilterForm(SavedFiltersMixin, FilterForm):
|
|
||||||
model = ObjectChange
|
|
||||||
fieldsets = (
|
|
||||||
FieldSet('q', 'filter_id'),
|
|
||||||
FieldSet('time_before', 'time_after', name=_('Time')),
|
|
||||||
FieldSet('action', 'user_id', 'changed_object_type_id', name=_('Attributes')),
|
|
||||||
)
|
|
||||||
time_after = forms.DateTimeField(
|
|
||||||
required=False,
|
|
||||||
label=_('After'),
|
|
||||||
widget=DateTimePicker()
|
|
||||||
)
|
|
||||||
time_before = forms.DateTimeField(
|
|
||||||
required=False,
|
|
||||||
label=_('Before'),
|
|
||||||
widget=DateTimePicker()
|
|
||||||
)
|
|
||||||
action = forms.ChoiceField(
|
|
||||||
label=_('Action'),
|
|
||||||
choices=add_blank_choice(ObjectChangeActionChoices),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
user_id = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=get_user_model().objects.all(),
|
|
||||||
required=False,
|
|
||||||
label=_('User')
|
|
||||||
)
|
|
||||||
changed_object_type_id = ContentTypeMultipleChoiceField(
|
|
||||||
queryset=ObjectType.objects.with_feature('change_logging'),
|
|
||||||
required=False,
|
|
||||||
label=_('Object Type'),
|
|
||||||
)
|
|
||||||
|
@ -64,7 +64,9 @@ class CustomFieldForm(forms.ModelForm):
|
|||||||
'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable', name=_('Behavior')
|
'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable', name=_('Behavior')
|
||||||
),
|
),
|
||||||
FieldSet('default', 'choice_set', name=_('Values')),
|
FieldSet('default', 'choice_set', name=_('Values')),
|
||||||
FieldSet('validation_minimum', 'validation_maximum', 'validation_regex', name=_('Validation')),
|
FieldSet(
|
||||||
|
'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation')
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -13,7 +13,6 @@ __all__ = (
|
|||||||
'ExportTemplateFilter',
|
'ExportTemplateFilter',
|
||||||
'ImageAttachmentFilter',
|
'ImageAttachmentFilter',
|
||||||
'JournalEntryFilter',
|
'JournalEntryFilter',
|
||||||
'ObjectChangeFilter',
|
|
||||||
'SavedFilterFilter',
|
'SavedFilterFilter',
|
||||||
'TagFilter',
|
'TagFilter',
|
||||||
'WebhookFilter',
|
'WebhookFilter',
|
||||||
@ -68,12 +67,6 @@ class JournalEntryFilter(BaseFilterMixin):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter(models.ObjectChange, lookups=True)
|
|
||||||
@autotype_decorator(filtersets.ObjectChangeFilterSet)
|
|
||||||
class ObjectChangeFilter(BaseFilterMixin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter(models.SavedFilter, lookups=True)
|
@strawberry_django.filter(models.SavedFilter, lookups=True)
|
||||||
@autotype_decorator(filtersets.SavedFilterFilterSet)
|
@autotype_decorator(filtersets.SavedFilterFilterSet)
|
||||||
class SavedFilterFilter(BaseFilterMixin):
|
class SavedFilterFilter(BaseFilterMixin):
|
||||||
|
@ -2,12 +2,8 @@ from typing import TYPE_CHECKING, Annotated, List
|
|||||||
|
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
from extras.models import ObjectChange
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ChangelogMixin',
|
|
||||||
'ConfigContextMixin',
|
'ConfigContextMixin',
|
||||||
'ContactsMixin',
|
'ContactsMixin',
|
||||||
'CustomFieldsMixin',
|
'CustomFieldsMixin',
|
||||||
@ -17,23 +13,10 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .types import ImageAttachmentType, JournalEntryType, ObjectChangeType, TagType
|
from .types import ImageAttachmentType, JournalEntryType, TagType
|
||||||
from tenancy.graphql.types import ContactAssignmentType
|
from tenancy.graphql.types import ContactAssignmentType
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
|
||||||
class ChangelogMixin:
|
|
||||||
|
|
||||||
@strawberry_django.field
|
|
||||||
def changelog(self, info) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]:
|
|
||||||
content_type = ContentType.objects.get_for_model(self)
|
|
||||||
object_changes = ObjectChange.objects.filter(
|
|
||||||
changed_object_type=content_type,
|
|
||||||
changed_object_id=self.pk
|
|
||||||
)
|
|
||||||
return object_changes.restrict(info.context.request.user, 'view')
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class ConfigContextMixin:
|
class ConfigContextMixin:
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ __all__ = (
|
|||||||
'ExportTemplateType',
|
'ExportTemplateType',
|
||||||
'ImageAttachmentType',
|
'ImageAttachmentType',
|
||||||
'JournalEntryType',
|
'JournalEntryType',
|
||||||
'ObjectChangeType',
|
|
||||||
'SavedFilterType',
|
'SavedFilterType',
|
||||||
'TagType',
|
'TagType',
|
||||||
'WebhookType',
|
'WebhookType',
|
||||||
@ -123,15 +122,6 @@ class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType):
|
|||||||
created_by: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
|
created_by: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
|
||||||
models.ObjectChange,
|
|
||||||
fields='__all__',
|
|
||||||
filters=ObjectChangeFilter
|
|
||||||
)
|
|
||||||
class ObjectChangeType(BaseObjectType):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
models.SavedFilter,
|
models.SavedFilter,
|
||||||
exclude=['content_types',],
|
exclude=['content_types',],
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user