nautobot 2.4.0__py3-none-any.whl → 2.4.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/__init__.py +1 -1
- nautobot/apps/api.py +8 -8
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/events.py +3 -3
- nautobot/apps/factory.py +2 -2
- nautobot/apps/filters.py +1 -1
- nautobot/apps/forms.py +20 -20
- nautobot/apps/graphql.py +2 -2
- nautobot/apps/jobs.py +8 -8
- nautobot/apps/models.py +19 -19
- nautobot/apps/tables.py +1 -1
- nautobot/apps/testing.py +10 -10
- nautobot/apps/ui.py +2 -2
- nautobot/apps/utils.py +7 -7
- nautobot/apps/views.py +7 -7
- nautobot/circuits/api/serializers.py +0 -1
- nautobot/circuits/api/views.py +8 -4
- nautobot/circuits/tables.py +1 -2
- nautobot/circuits/templates/circuits/circuit_create.html +7 -1
- nautobot/circuits/views.py +3 -3
- nautobot/cloud/api/views.py +10 -6
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/views.py +16 -0
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/filter_backends.py +9 -3
- nautobot/core/api/schema.py +2 -13
- nautobot/core/api/serializers.py +34 -40
- nautobot/core/api/views.py +4 -56
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +1 -1
- nautobot/core/choices.py +2 -2
- nautobot/core/events/__init__.py +3 -3
- nautobot/core/filters.py +16 -21
- nautobot/core/fixtures/user-data.json +59 -0
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +11 -14
- nautobot/core/forms/forms.py +2 -33
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +7 -28
- nautobot/core/jobs/cleanup.py +12 -48
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +3 -6
- nautobot/core/settings.py +2 -29
- nautobot/core/settings.yaml +0 -21
- nautobot/core/tables.py +60 -74
- nautobot/core/templates/inc/media.html +0 -3
- nautobot/core/templates/inc/nav_menu.html +0 -1
- nautobot/core/templates/nautobot_config.py.j2 +0 -13
- nautobot/core/templates/search.html +0 -7
- nautobot/core/templates/utilities/render_jinja2.html +1 -1
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +0 -7
- nautobot/core/templatetags/helpers.py +2 -11
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +15 -170
- nautobot/core/testing/filters.py +2 -25
- nautobot/core/testing/forms.py +0 -2
- nautobot/core/testing/mixins.py +2 -7
- nautobot/core/testing/views.py +29 -44
- nautobot/core/tests/integration/test_app_home.py +1 -0
- nautobot/core/tests/integration/test_app_navbar.py +1 -0
- nautobot/core/tests/integration/test_filters.py +2 -0
- nautobot/core/tests/integration/test_home.py +1 -0
- nautobot/core/tests/integration/test_navbar.py +1 -0
- nautobot/core/tests/integration/test_view_authentication.py +0 -1
- nautobot/core/tests/runner.py +1 -1
- nautobot/core/tests/test_api.py +1 -98
- nautobot/core/tests/test_csv.py +3 -25
- nautobot/core/tests/test_forms.py +0 -1
- nautobot/core/tests/test_jobs.py +1 -303
- nautobot/core/tests/test_settings_schema.py +0 -7
- nautobot/core/tests/test_tables.py +0 -100
- nautobot/core/tests/test_utils.py +1 -63
- nautobot/core/tests/test_views.py +3 -30
- nautobot/core/ui/nav.py +0 -1
- nautobot/core/ui/object_detail.py +1 -15
- nautobot/core/urls.py +0 -11
- nautobot/core/utils/lookup.py +8 -11
- nautobot/core/utils/requests.py +9 -24
- nautobot/core/views/__init__.py +0 -42
- nautobot/core/views/generic.py +176 -78
- nautobot/core/views/mixins.py +34 -94
- nautobot/core/views/renderers.py +6 -6
- nautobot/dcim/api/serializers.py +62 -54
- nautobot/dcim/api/views.py +113 -47
- nautobot/dcim/filters/__init__.py +2 -31
- nautobot/dcim/forms.py +17 -36
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/models/__init__.py +1 -1
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +20 -22
- nautobot/dcim/models/devices.py +1 -10
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +5 -6
- nautobot/dcim/models/racks.py +4 -4
- nautobot/dcim/tables/__init__.py +3 -3
- nautobot/dcim/tables/devices.py +5 -7
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller_create.html +7 -1
- nautobot/dcim/templates/dcim/controller_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -5
- nautobot/dcim/templates/dcim/device.html +9 -1
- nautobot/dcim/templates/dcim/device_edit.html +37 -36
- nautobot/dcim/templates/dcim/location.html +9 -1
- nautobot/dcim/templates/dcim/location_edit.html +7 -1
- nautobot/dcim/templates/dcim/rack.html +9 -1
- nautobot/dcim/templates/dcim/rack_edit.html +7 -1
- nautobot/dcim/templates/dcim/rackreservation.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +7 -1
- nautobot/dcim/tests/test_api.py +3 -16
- nautobot/dcim/tests/test_filters.py +0 -33
- nautobot/dcim/tests/test_forms.py +2 -51
- nautobot/dcim/tests/test_graphql.py +0 -52
- nautobot/dcim/tests/test_models.py +5 -34
- nautobot/dcim/tests/test_views.py +83 -21
- nautobot/dcim/views.py +13 -1
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +85 -90
- nautobot/extras/api/views.py +27 -22
- nautobot/extras/constants.py +0 -2
- nautobot/extras/filters/__init__.py +6 -8
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +31 -139
- nautobot/extras/forms/mixins.py +5 -13
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +2 -1
- nautobot/extras/jobs.py +12 -70
- nautobot/extras/managers.py +1 -3
- nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +11 -12
- nautobot/extras/models/groups.py +9 -11
- nautobot/extras/models/jobs.py +4 -23
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/plugins/__init__.py +2 -13
- nautobot/extras/plugins/marketplace_manifest.yml +79 -84
- nautobot/extras/plugins/tables.py +14 -16
- nautobot/extras/plugins/views.py +69 -65
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/tables.py +5 -7
- nautobot/extras/templates/extras/dynamicgroup.html +9 -1
- nautobot/extras/templates/extras/job_detail.html +0 -16
- nautobot/extras/templates/extras/job_edit.html +0 -1
- nautobot/extras/templates/extras/jobqueue_retrieve.html +9 -1
- nautobot/extras/templates/extras/marketplace.html +11 -29
- nautobot/extras/templates/extras/plugin_detail.html +15 -32
- nautobot/extras/templates/extras/plugins_tiles.html +10 -21
- nautobot/extras/test_jobs/api_test_job.py +1 -1
- nautobot/extras/test_jobs/atomic_transaction.py +2 -2
- nautobot/extras/test_jobs/dry_run.py +1 -1
- nautobot/extras/test_jobs/fail.py +5 -5
- nautobot/extras/test_jobs/file_output.py +1 -1
- nautobot/extras/test_jobs/file_upload_fail.py +1 -1
- nautobot/extras/test_jobs/file_upload_pass.py +1 -1
- nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
- nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
- nautobot/extras/test_jobs/log_redaction.py +1 -1
- nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
- nautobot/extras/test_jobs/modify_db.py +1 -1
- nautobot/extras/test_jobs/object_var_optional.py +1 -1
- nautobot/extras/test_jobs/object_var_required.py +1 -1
- nautobot/extras/test_jobs/object_vars.py +1 -1
- nautobot/extras/test_jobs/pass.py +3 -3
- nautobot/extras/test_jobs/profiling.py +1 -1
- nautobot/extras/test_jobs/relative_import.py +3 -3
- nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
- nautobot/extras/test_jobs/task_queues.py +1 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
- nautobot/extras/tests/test_api.py +13 -13
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +1 -2
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +6 -6
- nautobot/extras/tests/test_forms.py +1 -20
- nautobot/extras/tests/test_jobs.py +19 -160
- nautobot/extras/tests/test_models.py +10 -10
- nautobot/extras/tests/test_plugins.py +9 -62
- nautobot/extras/tests/test_relationships.py +9 -120
- nautobot/extras/tests/test_views.py +191 -52
- nautobot/extras/utils.py +2 -3
- nautobot/extras/views.py +98 -30
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +33 -41
- nautobot/ipam/api/views.py +117 -68
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +2 -3
- nautobot/ipam/lookups.py +62 -101
- nautobot/ipam/models.py +16 -66
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +7 -23
- nautobot/ipam/templates/ipam/ipaddress.html +9 -1
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +7 -1
- nautobot/ipam/templates/ipam/ipaddress_edit.html +7 -1
- nautobot/ipam/templates/ipam/prefix.html +9 -1
- nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
- nautobot/ipam/templates/ipam/vlan.html +9 -1
- nautobot/ipam/templates/ipam/vlan_edit.html +7 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +7 -1
- nautobot/ipam/tests/test_api.py +3 -416
- nautobot/ipam/tests/test_forms.py +47 -49
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +34 -95
- nautobot/ipam/tests/test_querysets.py +1 -63
- nautobot/ipam/tests/test_views.py +0 -3
- nautobot/ipam/utils/__init__.py +6 -36
- nautobot/ipam/views.py +87 -61
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +2 -40
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/docs/404.html +4 -46
- nautobot/project-static/docs/apps/index.html +4 -46
- nautobot/project-static/docs/apps/nautobot-apps.html +6 -47
- nautobot/project-static/docs/assets/_mkdocstrings.css +1 -25
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -62
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +122 -374
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -90
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +21 -95
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +6 -53
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +5 -52
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +17 -79
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +28 -102
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +21 -108
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +38 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +65 -239
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +165 -581
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +36 -109
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +167 -453
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +211 -493
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +8 -60
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +15 -71
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +55 -407
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +205 -585
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +412 -858
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +186 -448
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +147 -365
- nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -46
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +7 -68
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -46
- nautobot/project-static/docs/development/apps/api/prometheus.html +4 -46
- nautobot/project-static/docs/development/apps/api/setup.html +4 -46
- nautobot/project-static/docs/development/apps/api/testing.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/notes.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +6 -52
- nautobot/project-static/docs/development/apps/api/views/urls.html +4 -46
- nautobot/project-static/docs/development/apps/index.html +4 -46
- nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +8 -50
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +14 -211
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -46
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -46
- nautobot/project-static/docs/development/core/application-registry.html +4 -46
- nautobot/project-static/docs/development/core/best-practices.html +4 -46
- nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -46
- nautobot/project-static/docs/development/core/caching.html +4 -46
- nautobot/project-static/docs/development/core/controllers.html +4 -46
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +74 -73
- nautobot/project-static/docs/development/core/generic-views.html +4 -46
- nautobot/project-static/docs/development/core/getting-started.html +224 -249
- nautobot/project-static/docs/development/core/homepage.html +7 -49
- nautobot/project-static/docs/development/core/index.html +4 -46
- nautobot/project-static/docs/development/core/{minikube-dev-environment-for-k8s-jobs.html → local-k8s.html} +168 -469
- nautobot/project-static/docs/development/core/model-checklist.html +12 -56
- nautobot/project-static/docs/development/core/model-features.html +4 -46
- nautobot/project-static/docs/development/core/natural-keys.html +4 -46
- nautobot/project-static/docs/development/core/navigation-menu.html +4 -46
- nautobot/project-static/docs/development/core/release-checklist.html +7 -49
- nautobot/project-static/docs/development/core/role-internals.html +4 -46
- nautobot/project-static/docs/development/core/settings.html +4 -46
- nautobot/project-static/docs/development/core/style-guide.html +7 -49
- nautobot/project-static/docs/development/core/templates.html +4 -46
- nautobot/project-static/docs/development/core/testing.html +4 -46
- nautobot/project-static/docs/development/core/ui-component-framework.html +273 -369
- nautobot/project-static/docs/development/core/user-preferences.html +4 -46
- nautobot/project-static/docs/development/index.html +4 -46
- nautobot/project-static/docs/development/jobs/index.html +122 -216
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -46
- nautobot/project-static/docs/index.html +23 -54
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +7 -47
- nautobot/project-static/docs/overview/design_philosophy.html +4 -46
- nautobot/project-static/docs/release-notes/index.html +12 -52
- nautobot/project-static/docs/release-notes/version-1.0.html +193 -234
- nautobot/project-static/docs/release-notes/version-1.1.html +190 -231
- nautobot/project-static/docs/release-notes/version-1.2.html +265 -306
- nautobot/project-static/docs/release-notes/version-1.3.html +291 -332
- nautobot/project-static/docs/release-notes/version-1.4.html +377 -417
- nautobot/project-static/docs/release-notes/version-1.5.html +566 -605
- nautobot/project-static/docs/release-notes/version-1.6.html +447 -904
- nautobot/project-static/docs/release-notes/version-2.0.html +489 -528
- nautobot/project-static/docs/release-notes/version-2.1.html +324 -363
- nautobot/project-static/docs/release-notes/version-2.2.html +317 -356
- nautobot/project-static/docs/release-notes/version-2.3.html +352 -997
- nautobot/project-static/docs/release-notes/version-2.4.html +101 -417
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +287 -295
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +6 -48
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +8 -110
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +6 -48
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +8 -66
- nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +5 -47
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +8 -49
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +12 -50
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +7 -49
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +7 -51
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +7 -49
- nautobot/project-static/docs/user-guide/index.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +8 -50
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +7 -50
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +7 -49
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +5 -47
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -94
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +5 -74
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -46
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +13 -9
- nautobot/tenancy/views.py +2 -4
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +4 -5
- nautobot/users/api/views.py +3 -3
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/api/views.py +24 -5
- nautobot/virtualization/filters.py +3 -20
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +7 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +9 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +8 -2
- nautobot/virtualization/tests/test_filters.py +0 -17
- nautobot/wireless/filters.py +2 -2
- nautobot/wireless/forms.py +1 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +9 -1
- nautobot/wireless/tests/test_filters.py +1 -29
- nautobot/wireless/views.py +10 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +6 -6
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +543 -591
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
- nautobot/core/api/constants.py +0 -11
- nautobot/core/jobs/bulk_actions.py +0 -248
- nautobot/core/templates/about.html +0 -67
- nautobot/core/templates/inc/tenancy_form_panel.html +0 -9
- nautobot/core/templates/inc/tenant_table_row.html +0 -11
- nautobot/core/utils/querysets.py +0 -64
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +0 -25
- nautobot/dcim/tests/integration/test_controller.py +0 -62
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +0 -71
- nautobot/dcim/tests/test_jobs.py +0 -118
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +0 -22
- nautobot/extras/migrations/0121_alter_team_contacts.py +0 -17
- nautobot/extras/test_jobs/singleton.py +0 -16
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -9444
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -9722
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +0 -42
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/cloud/views.py
CHANGED
|
@@ -100,6 +100,16 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
|
|
|
100
100
|
|
|
101
101
|
return context
|
|
102
102
|
|
|
103
|
+
def extra_post_save_action(self, obj, form):
|
|
104
|
+
if form.cleaned_data.get("add_prefixes", None):
|
|
105
|
+
obj.prefixes.add(*form.cleaned_data["add_prefixes"])
|
|
106
|
+
if form.cleaned_data.get("remove_prefixes", None):
|
|
107
|
+
obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
|
|
108
|
+
if form.cleaned_data.get("add_cloud_services", None):
|
|
109
|
+
obj.cloud_services.add(*form.cleaned_data["add_cloud_services"])
|
|
110
|
+
if form.cleaned_data.get("remove_cloud_services", None):
|
|
111
|
+
obj.cloud_services.remove(*form.cleaned_data["remove_cloud_services"])
|
|
112
|
+
|
|
103
113
|
|
|
104
114
|
class CloudResourceTypeUIViewSet(NautobotUIViewSet):
|
|
105
115
|
queryset = CloudResourceType.objects.all()
|
|
@@ -163,3 +173,9 @@ class CloudServiceUIViewSet(NautobotUIViewSet):
|
|
|
163
173
|
)
|
|
164
174
|
|
|
165
175
|
return context
|
|
176
|
+
|
|
177
|
+
def extra_post_save_action(self, obj, form):
|
|
178
|
+
if form.cleaned_data.get("add_cloud_networks", None):
|
|
179
|
+
obj.cloud_networks.add(*form.cleaned_data["add_cloud_networks"])
|
|
180
|
+
if form.cleaned_data.get("remove_cloud_networks", None):
|
|
181
|
+
obj.cloud_networks.remove(*form.cleaned_data["remove_cloud_networks"])
|
nautobot/core/api/fields.py
CHANGED
|
@@ -53,10 +53,10 @@ class ChoiceField(serializers.Field):
|
|
|
53
53
|
data = ""
|
|
54
54
|
return super().validate_empty_values(data)
|
|
55
55
|
|
|
56
|
-
def to_representation(self,
|
|
57
|
-
if
|
|
56
|
+
def to_representation(self, obj):
|
|
57
|
+
if obj == "":
|
|
58
58
|
return None
|
|
59
|
-
return OrderedDict([("value",
|
|
59
|
+
return OrderedDict([("value", obj), ("label", self._choices[obj])])
|
|
60
60
|
|
|
61
61
|
def to_internal_value(self, data):
|
|
62
62
|
if data == "":
|
|
@@ -123,8 +123,8 @@ class ContentTypeField(RelatedField):
|
|
|
123
123
|
self.fail("invalid")
|
|
124
124
|
return None
|
|
125
125
|
|
|
126
|
-
def to_representation(self,
|
|
127
|
-
return f"{
|
|
126
|
+
def to_representation(self, obj):
|
|
127
|
+
return f"{obj.app_label}.{obj.model}"
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
class LaxURLField(URLField):
|
|
@@ -2,8 +2,6 @@ from django_filters.rest_framework.backends import DjangoFilterBackend
|
|
|
2
2
|
from rest_framework.filters import OrderingFilter
|
|
3
3
|
from tree_queries.models import TreeNode
|
|
4
4
|
|
|
5
|
-
from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
class NautobotFilterBackend(DjangoFilterBackend):
|
|
9
7
|
"""Custom filtering backend for use with django-rest-framework and django-filters."""
|
|
@@ -19,7 +17,15 @@ class NautobotFilterBackend(DjangoFilterBackend):
|
|
|
19
17
|
# The default 'data' is a reference to request.GET, which is immutable; copies of this data are mutable
|
|
20
18
|
data = kwargs["data"].copy()
|
|
21
19
|
|
|
22
|
-
for non_filter_param in
|
|
20
|
+
for non_filter_param in (
|
|
21
|
+
"api_version", # used to select the Nautobot API version
|
|
22
|
+
"depth", # nested levels of the serializers default to depth=0
|
|
23
|
+
"format", # "json" or "api", used in the interactive HTML REST API views
|
|
24
|
+
"include", # used to include computed fields, relationships, config-contexts, etc. (excluded by default)
|
|
25
|
+
"limit", # pagination
|
|
26
|
+
"offset", # pagination
|
|
27
|
+
"sort", # sorting of results
|
|
28
|
+
):
|
|
23
29
|
data.pop(non_filter_param, None)
|
|
24
30
|
|
|
25
31
|
kwargs["data"] = data
|
nautobot/core/api/schema.py
CHANGED
|
@@ -112,13 +112,13 @@ class NautobotAutoSchema(AutoSchema):
|
|
|
112
112
|
"required": True,
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
# Inject a custom description for the "id" parameter since ours has custom lookup behavior.
|
|
115
116
|
if operation is not None and "parameters" in operation:
|
|
116
117
|
for param in operation["parameters"]:
|
|
117
|
-
# Inject a custom description for the "id" parameter since ours has custom lookup behavior.
|
|
118
118
|
if param["name"] == "id" and "description" not in param:
|
|
119
119
|
param["description"] = "Unique object identifier, either a UUID primary key or a composite key."
|
|
120
120
|
if self.method == "GET":
|
|
121
|
-
if
|
|
121
|
+
if "depth" not in operation["parameters"]:
|
|
122
122
|
operation["parameters"].append(
|
|
123
123
|
{
|
|
124
124
|
"in": "query",
|
|
@@ -128,17 +128,6 @@ class NautobotAutoSchema(AutoSchema):
|
|
|
128
128
|
"schema": {"type": "integer", "minimum": 0, "maximum": 10, "default": 1},
|
|
129
129
|
}
|
|
130
130
|
)
|
|
131
|
-
if not any(param["name"] == "exclude_m2m" for param in operation["parameters"]):
|
|
132
|
-
operation["parameters"].append(
|
|
133
|
-
{
|
|
134
|
-
"in": "query",
|
|
135
|
-
"name": "exclude_m2m",
|
|
136
|
-
"required": False,
|
|
137
|
-
"description": "Exclude many-to-many fields from the response",
|
|
138
|
-
"schema": {"type": "boolean", "default": False},
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
# TODO: add "include" parameter to the schema where relevant - nautobot/nautobot#685
|
|
142
131
|
return operation
|
|
143
132
|
|
|
144
133
|
def get_operation_id(self):
|
nautobot/core/api/serializers.py
CHANGED
|
@@ -33,7 +33,6 @@ from nautobot.core.api.utils import (
|
|
|
33
33
|
from nautobot.core.models.fields import LaxURLField as LaxURLModelField
|
|
34
34
|
from nautobot.core.models.managers import TagsManager
|
|
35
35
|
from nautobot.core.models.utils import construct_composite_key, construct_natural_slug
|
|
36
|
-
from nautobot.core.settings_funcs import is_truthy
|
|
37
36
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
38
37
|
from nautobot.core.utils.requests import normalize_querydict
|
|
39
38
|
from nautobot.extras.api.customfields import CustomFieldDefaultValues, CustomFieldsDataField
|
|
@@ -47,9 +46,8 @@ logger = logging.getLogger(__name__)
|
|
|
47
46
|
|
|
48
47
|
class OptInFieldsMixin:
|
|
49
48
|
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
The serializer's `Meta.opt_in_fields` controls which fields are influenced by `include`.
|
|
49
|
+
A serializer mixin that takes an additional `opt_in_fields` argument that controls
|
|
50
|
+
which fields should be displayed.
|
|
53
51
|
"""
|
|
54
52
|
|
|
55
53
|
def __init__(self, *args, **kwargs):
|
|
@@ -59,11 +57,8 @@ class OptInFieldsMixin:
|
|
|
59
57
|
@property
|
|
60
58
|
def fields(self):
|
|
61
59
|
"""
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- Removes all serializer fields specified in `Meta.opt_in_fields` list that aren't specified in the
|
|
65
|
-
`include` query parameter. (applies to GET requests only)
|
|
66
|
-
- If the `exclude_m2m` query parameter is truthy, remove all many-to-many serializer fields for performance.
|
|
60
|
+
Removes all serializer fields specified in a serializers `opt_in_fields` list that aren't specified in the
|
|
61
|
+
`include` query parameter.
|
|
67
62
|
|
|
68
63
|
As an example, if the serializer specifies that `opt_in_fields = ["computed_fields"]`
|
|
69
64
|
but `computed_fields` is not specified in the `?include` query parameter, `computed_fields` will be popped
|
|
@@ -71,7 +66,12 @@ class OptInFieldsMixin:
|
|
|
71
66
|
"""
|
|
72
67
|
if self.__pruned_fields is None:
|
|
73
68
|
fields = dict(super().fields)
|
|
74
|
-
serializer_opt_in_fields = getattr(self.Meta, "opt_in_fields",
|
|
69
|
+
serializer_opt_in_fields = getattr(self.Meta, "opt_in_fields", None)
|
|
70
|
+
|
|
71
|
+
if not serializer_opt_in_fields:
|
|
72
|
+
# This serializer has no defined opt_in_fields, so we never need to go further than this
|
|
73
|
+
self.__pruned_fields = fields
|
|
74
|
+
return self.__pruned_fields
|
|
75
75
|
|
|
76
76
|
if not hasattr(self, "_context"):
|
|
77
77
|
# We are being called before a request cycle
|
|
@@ -83,29 +83,24 @@ class OptInFieldsMixin:
|
|
|
83
83
|
# No available request?
|
|
84
84
|
return fields
|
|
85
85
|
|
|
86
|
+
# opt-in fields only applies on GET requests, for other methods we support these fields regardless
|
|
87
|
+
if request is not None and request.method != "GET":
|
|
88
|
+
return fields
|
|
89
|
+
|
|
86
90
|
# NOTE: drf test framework builds a request object where the query
|
|
87
91
|
# parameters are found under the GET attribute.
|
|
88
92
|
params = normalize_querydict(getattr(request, "query_params", getattr(request, "GET", None)))
|
|
89
93
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if field not in user_opt_in_fields:
|
|
101
|
-
fields.pop(field, None)
|
|
102
|
-
|
|
103
|
-
# If exclude_m2m is present and truthy, mark any many-to-many fields as write-only so they
|
|
104
|
-
# don't get included in the response.
|
|
105
|
-
if is_truthy(params.get("exclude_m2m", "false")):
|
|
106
|
-
for field_instance in fields.values():
|
|
107
|
-
if isinstance(field_instance, (serializers.ManyRelatedField, serializers.ListSerializer)):
|
|
108
|
-
field_instance.write_only = True
|
|
94
|
+
try:
|
|
95
|
+
user_opt_in_fields = params.get("include", [])
|
|
96
|
+
except AttributeError:
|
|
97
|
+
# include parameter was not specified
|
|
98
|
+
user_opt_in_fields = []
|
|
99
|
+
|
|
100
|
+
# Drop any fields that are not specified in the users opt in fields
|
|
101
|
+
for field in serializer_opt_in_fields:
|
|
102
|
+
if field not in user_opt_in_fields:
|
|
103
|
+
fields.pop(field, None)
|
|
109
104
|
|
|
110
105
|
self.__pruned_fields = fields
|
|
111
106
|
|
|
@@ -136,7 +131,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
|
|
|
136
131
|
|
|
137
132
|
serializer_related_field = NautobotHyperlinkedRelatedField
|
|
138
133
|
|
|
139
|
-
id = serializers.UUIDField(read_only=False, default=serializers.CreateOnlyDefault(uuid.uuid4))
|
|
140
134
|
display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
|
|
141
135
|
object_type = ObjectTypeField()
|
|
142
136
|
# composite_key = serializers.SerializerMethodField() # TODO: Revisit if we reintroduce composite keys
|
|
@@ -531,27 +525,27 @@ class ValidatedModelSerializer(BaseModelSerializer):
|
|
|
531
525
|
validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
|
|
532
526
|
"""
|
|
533
527
|
|
|
534
|
-
def validate(self,
|
|
528
|
+
def validate(self, data):
|
|
535
529
|
# Remove custom fields data and tags (if any) prior to model validation
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
530
|
+
attrs = data.copy()
|
|
531
|
+
attrs.pop("custom_fields", None)
|
|
532
|
+
attrs.pop("relationships", None)
|
|
533
|
+
attrs.pop("tags", None)
|
|
540
534
|
|
|
541
535
|
# Skip ManyToManyFields
|
|
542
536
|
for field in self.Meta.model._meta.get_fields():
|
|
543
537
|
if isinstance(field, models.ManyToManyField):
|
|
544
|
-
|
|
538
|
+
attrs.pop(field.name, None)
|
|
545
539
|
|
|
546
540
|
# Run clean() on an instance of the model
|
|
547
541
|
if self.instance is None:
|
|
548
|
-
instance = self.Meta.model(**
|
|
542
|
+
instance = self.Meta.model(**attrs)
|
|
549
543
|
else:
|
|
550
544
|
instance = self.instance
|
|
551
|
-
for k, v in
|
|
545
|
+
for k, v in attrs.items():
|
|
552
546
|
setattr(instance, k, v)
|
|
553
547
|
instance.full_clean()
|
|
554
|
-
return
|
|
548
|
+
return data
|
|
555
549
|
|
|
556
550
|
|
|
557
551
|
class WritableNestedSerializer(BaseModelSerializer):
|
|
@@ -597,7 +591,7 @@ class WritableNestedSerializer(BaseModelSerializer):
|
|
|
597
591
|
pk = None
|
|
598
592
|
|
|
599
593
|
if isinstance(self.Meta.model._meta.pk, models.AutoField):
|
|
600
|
-
# PK is an int for this model. This is usually the
|
|
594
|
+
# PK is an int for this model. This is usually the User model
|
|
601
595
|
try:
|
|
602
596
|
pk = int(data)
|
|
603
597
|
except (TypeError, ValueError):
|
nautobot/core/api/views.py
CHANGED
|
@@ -7,11 +7,9 @@ from django import __version__ as DJANGO_VERSION, forms
|
|
|
7
7
|
from django.apps import apps
|
|
8
8
|
from django.conf import settings
|
|
9
9
|
from django.contrib.contenttypes.models import ContentType
|
|
10
|
-
from django.core.exceptions import
|
|
10
|
+
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
11
11
|
from django.db import transaction
|
|
12
12
|
from django.db.models import ProtectedError
|
|
13
|
-
from django.db.models.fields.related import ForeignKey, ManyToManyField, RelatedField
|
|
14
|
-
from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
|
|
15
13
|
from django.http.response import HttpResponseBadRequest
|
|
16
14
|
from django.shortcuts import get_object_or_404, redirect
|
|
17
15
|
from drf_spectacular.plumbing import get_relative_url, set_query_parameters
|
|
@@ -25,7 +23,7 @@ from graphql.execution import ExecutionResult
|
|
|
25
23
|
from graphql.execution.middleware import MiddlewareManager
|
|
26
24
|
from graphql.type.schema import GraphQLSchema
|
|
27
25
|
import redis.exceptions
|
|
28
|
-
from rest_framework import routers,
|
|
26
|
+
from rest_framework import routers, status
|
|
29
27
|
from rest_framework.exceptions import APIException, ParseError, PermissionDenied
|
|
30
28
|
from rest_framework.generics import GenericAPIView
|
|
31
29
|
from rest_framework.permissions import IsAuthenticated
|
|
@@ -40,11 +38,9 @@ from nautobot.core.api.exceptions import SerializerNotFound
|
|
|
40
38
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
41
39
|
from nautobot.core.celery import app as celery_app
|
|
42
40
|
from nautobot.core.exceptions import FilterSetFieldNotFound
|
|
43
|
-
from nautobot.core.models.fields import TagsField
|
|
44
41
|
from nautobot.core.utils.data import is_uuid, render_jinja2
|
|
45
42
|
from nautobot.core.utils.filtering import get_all_lookup_expr_for_field, get_filterset_parameter_form_field
|
|
46
43
|
from nautobot.core.utils.lookup import get_form_for_model
|
|
47
|
-
from nautobot.core.utils.querysets import maybe_prefetch_related, maybe_select_related
|
|
48
44
|
from nautobot.core.utils.requests import ensure_content_type_and_field_name_in_query_params
|
|
49
45
|
from nautobot.core.views.utils import get_csv_form_fields_from_serializer_class
|
|
50
46
|
|
|
@@ -236,53 +232,6 @@ class ModelViewSetMixin:
|
|
|
236
232
|
|
|
237
233
|
return context
|
|
238
234
|
|
|
239
|
-
def get_queryset(self):
|
|
240
|
-
"""
|
|
241
|
-
Attempt to optimize the queryset based on the fields present in the associated serializer.
|
|
242
|
-
|
|
243
|
-
See similar logic in nautobot.core.tables.BaseTable.
|
|
244
|
-
"""
|
|
245
|
-
queryset = super().get_queryset()
|
|
246
|
-
model = queryset.model
|
|
247
|
-
serializer = self.get_serializer()
|
|
248
|
-
|
|
249
|
-
select_fields = []
|
|
250
|
-
prefetch_fields = []
|
|
251
|
-
|
|
252
|
-
for field_instance in serializer.fields.values():
|
|
253
|
-
if field_instance.write_only:
|
|
254
|
-
continue
|
|
255
|
-
if field_instance.source == "*":
|
|
256
|
-
continue
|
|
257
|
-
if "." in field_instance.source:
|
|
258
|
-
# DRF uses `field.nested_field` instead of `field__nested_field`
|
|
259
|
-
# TODO: We don't currently attempt to optimize nested lookups.
|
|
260
|
-
continue
|
|
261
|
-
if isinstance(field_instance, (drf_serializers.ManyRelatedField, drf_serializers.ListSerializer)):
|
|
262
|
-
# ListSerializer with depth > 0, ManyRelatedField with depth 0
|
|
263
|
-
try:
|
|
264
|
-
model_field = model._meta.get_field(field_instance.source)
|
|
265
|
-
except FieldDoesNotExist:
|
|
266
|
-
continue
|
|
267
|
-
if isinstance(model_field, (ManyToManyField, ManyToManyRel, RelatedField, ManyToOneRel, TagsField)):
|
|
268
|
-
prefetch_fields.append(field_instance.source)
|
|
269
|
-
elif isinstance(field_instance, (drf_serializers.RelatedField, drf_serializers.Serializer)):
|
|
270
|
-
# Serializer with depth > 0, RelatedField with depth 0
|
|
271
|
-
try:
|
|
272
|
-
model_field = model._meta.get_field(field_instance.source)
|
|
273
|
-
except FieldDoesNotExist:
|
|
274
|
-
continue
|
|
275
|
-
if isinstance(model_field, ForeignKey):
|
|
276
|
-
select_fields.append(field_instance.source)
|
|
277
|
-
|
|
278
|
-
if select_fields:
|
|
279
|
-
queryset = maybe_select_related(queryset, select_fields)
|
|
280
|
-
|
|
281
|
-
if prefetch_fields:
|
|
282
|
-
queryset = maybe_prefetch_related(queryset, prefetch_fields)
|
|
283
|
-
|
|
284
|
-
return queryset
|
|
285
|
-
|
|
286
235
|
def restrict_queryset(self, request, *args, **kwargs):
|
|
287
236
|
"""
|
|
288
237
|
Restrict the view's queryset to allow only the permitted objects for the given request.
|
|
@@ -421,7 +370,7 @@ class APIRootView(AuthenticatedAPIRootView):
|
|
|
421
370
|
name = "API Root"
|
|
422
371
|
|
|
423
372
|
@extend_schema(exclude=True)
|
|
424
|
-
def get(self, request,
|
|
373
|
+
def get(self, request, format=None): # pylint: disable=redefined-builtin
|
|
425
374
|
return Response(
|
|
426
375
|
OrderedDict(
|
|
427
376
|
(
|
|
@@ -628,13 +577,12 @@ class GraphQLDRFAPIView(NautobotAPIVersionMixin, APIView):
|
|
|
628
577
|
middleware = None
|
|
629
578
|
root_value = None
|
|
630
579
|
|
|
631
|
-
def __init__(self, schema=None, executor=None, middleware=None, root_value=None, backend=None
|
|
580
|
+
def __init__(self, schema=None, executor=None, middleware=None, root_value=None, backend=None):
|
|
632
581
|
self.schema = schema
|
|
633
582
|
self.executor = executor
|
|
634
583
|
self.middleware = middleware
|
|
635
584
|
self.root_value = root_value
|
|
636
585
|
self.backend = backend
|
|
637
|
-
super().__init__(**kwargs)
|
|
638
586
|
|
|
639
587
|
def get_root_value(self, request):
|
|
640
588
|
return self.root_value
|
nautobot/core/celery/log.py
CHANGED
|
@@ -11,10 +11,6 @@ class NautobotDatabaseHandler(logging.Handler):
|
|
|
11
11
|
if current_task is None:
|
|
12
12
|
return
|
|
13
13
|
|
|
14
|
-
# Skip recording the log entry if it has been marked as such
|
|
15
|
-
if getattr(record, "skip_db_logging", False):
|
|
16
|
-
return
|
|
17
|
-
|
|
18
14
|
from nautobot.extras.models.jobs import JobResult
|
|
19
15
|
|
|
20
16
|
try:
|
|
@@ -28,6 +24,10 @@ class NautobotDatabaseHandler(logging.Handler):
|
|
|
28
24
|
# JobResult.DoesNotExist - because we might not have a JobResult with that ID
|
|
29
25
|
return
|
|
30
26
|
|
|
27
|
+
# Skip recording the log entry if it has been marked as such
|
|
28
|
+
if getattr(record, "skip_db_logging", False):
|
|
29
|
+
return
|
|
30
|
+
|
|
31
31
|
job_result.log(
|
|
32
32
|
message=record.message,
|
|
33
33
|
level_choice=record.levelname.lower(),
|
|
@@ -25,7 +25,7 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
25
25
|
nautobot.extras.models.ScheduledJob model
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
def __init__(self, model, app=None):
|
|
28
|
+
def __init__(self, model, app=None):
|
|
29
29
|
"""Initialize the model entry."""
|
|
30
30
|
# copy-paste from django_celery_beat.schedulers
|
|
31
31
|
self.app = app or current_app._get_current_object()
|
nautobot/core/choices.py
CHANGED
|
@@ -182,14 +182,14 @@ class ButtonActionColorChoices(ChoiceSet):
|
|
|
182
182
|
Map standard button actions to Bootstrap color classes.
|
|
183
183
|
"""
|
|
184
184
|
|
|
185
|
-
ADD = "
|
|
185
|
+
ADD = "info"
|
|
186
186
|
CANCEL = "default"
|
|
187
187
|
CLONE = "success"
|
|
188
188
|
CONFIGURE = "default"
|
|
189
189
|
CONNECT = "success"
|
|
190
190
|
DEFAULT = "default"
|
|
191
191
|
DELETE = "danger"
|
|
192
|
-
DISCONNECT = "
|
|
192
|
+
DISCONNECT = "info"
|
|
193
193
|
EDIT = "warning"
|
|
194
194
|
EXPORT = "success"
|
|
195
195
|
IMPORT = "primary"
|
nautobot/core/events/__init__.py
CHANGED
|
@@ -107,10 +107,10 @@ def publish_event(*, topic, payload):
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
__all__ = (
|
|
110
|
-
"EventBroker",
|
|
111
|
-
"RedisEventBroker",
|
|
112
|
-
"SyslogEventBroker",
|
|
113
110
|
"deregister_event_broker",
|
|
111
|
+
"EventBroker",
|
|
114
112
|
"publish_event",
|
|
113
|
+
"RedisEventBroker",
|
|
115
114
|
"register_event_broker",
|
|
115
|
+
"SyslogEventBroker",
|
|
116
116
|
)
|
nautobot/core/filters.py
CHANGED
|
@@ -618,7 +618,6 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
618
618
|
@staticmethod
|
|
619
619
|
def _get_filter_lookup_dict(existing_filter):
|
|
620
620
|
# Choose the lookup expression map based on the filter type
|
|
621
|
-
|
|
622
621
|
if isinstance(
|
|
623
622
|
existing_filter,
|
|
624
623
|
(
|
|
@@ -638,7 +637,6 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
638
637
|
(
|
|
639
638
|
django_filters.ModelChoiceFilter,
|
|
640
639
|
django_filters.ModelMultipleChoiceFilter,
|
|
641
|
-
MultiValueUUIDFilter,
|
|
642
640
|
TagFilter,
|
|
643
641
|
TreeNodeMultipleChoiceFilter,
|
|
644
642
|
),
|
|
@@ -668,7 +666,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
668
666
|
the form `<field_name>__<lookup_expr>`
|
|
669
667
|
"""
|
|
670
668
|
magic_filters = {}
|
|
671
|
-
if filter_field.method is not None or filter_field.lookup_expr not in ["exact", "in"
|
|
669
|
+
if filter_field.method is not None or filter_field.lookup_expr not in ["exact", "in"]:
|
|
672
670
|
return magic_filters
|
|
673
671
|
|
|
674
672
|
# Choose the lookup expression map based on the filter type
|
|
@@ -679,7 +677,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
679
677
|
|
|
680
678
|
# Get properties of the existing filter for later use
|
|
681
679
|
field_name = filter_field.field_name
|
|
682
|
-
field = get_model_field(cls._meta.model, field_name)
|
|
680
|
+
field = get_model_field(cls._meta.model, field_name)
|
|
683
681
|
|
|
684
682
|
# If there isn't a model field, return.
|
|
685
683
|
if field is None:
|
|
@@ -696,7 +694,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
696
694
|
new_filter_name = f"{filter_name}__{lookup_name}"
|
|
697
695
|
|
|
698
696
|
try:
|
|
699
|
-
if filter_name in cls.declared_filters and lookup_expr not in {"isnull"}:
|
|
697
|
+
if filter_name in cls.declared_filters and lookup_expr not in {"isnull"}:
|
|
700
698
|
# The filter field has been explicitly defined on the filterset class so we must manually
|
|
701
699
|
# create the new filter with the same type because there is no guarantee the defined type
|
|
702
700
|
# is the same as the default type for the field. This does not apply if the filter
|
|
@@ -727,10 +725,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
727
725
|
# If the base filter_field has a custom label, django_filters won't adjust it for the new_filter lookup,
|
|
728
726
|
# so we have to do it.
|
|
729
727
|
if filter_field.label and filter_field.label != label_for_filter(
|
|
730
|
-
cls.
|
|
731
|
-
filter_field.field_name,
|
|
732
|
-
filter_field.lookup_expr,
|
|
733
|
-
filter_field.exclude,
|
|
728
|
+
cls.Meta.model, filter_field.field_name, filter_field.lookup_expr, filter_field.exclude
|
|
734
729
|
):
|
|
735
730
|
# Lightly adjusted from label_for_filter() implementation:
|
|
736
731
|
verbose_expression = ["exclude", filter_field.label] if new_filter.exclude else [filter_field.label]
|
|
@@ -753,22 +748,22 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
753
748
|
if not isinstance(new_filter_field, django_filters.Filter):
|
|
754
749
|
raise TypeError(f"Tried to add filter ({new_filter_name}) which is not an instance of Django Filter")
|
|
755
750
|
|
|
756
|
-
if new_filter_name in cls.base_filters:
|
|
751
|
+
if new_filter_name in cls.base_filters:
|
|
757
752
|
raise AttributeError(
|
|
758
753
|
f"There was a conflict with filter `{new_filter_name}`, the custom filter was ignored."
|
|
759
754
|
)
|
|
760
755
|
|
|
761
|
-
cls.base_filters[new_filter_name] = new_filter_field
|
|
756
|
+
cls.base_filters[new_filter_name] = new_filter_field
|
|
762
757
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
763
|
-
if cls._meta.model is not None:
|
|
764
|
-
cls.base_filters.update(
|
|
758
|
+
if cls._meta.model is not None:
|
|
759
|
+
cls.base_filters.update(
|
|
765
760
|
cls._generate_lookup_expression_filters(filter_name=new_filter_name, filter_field=new_filter_field)
|
|
766
761
|
)
|
|
767
762
|
|
|
768
763
|
@classmethod
|
|
769
764
|
def get_fields(cls):
|
|
770
765
|
fields = super().get_fields()
|
|
771
|
-
if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude):
|
|
766
|
+
if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude):
|
|
772
767
|
# Add "id" as the first key in the `fields` dict
|
|
773
768
|
fields = {"id": [django_filters.conf.settings.DEFAULT_LOOKUP_EXPR], **fields}
|
|
774
769
|
return fields
|
|
@@ -785,7 +780,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
785
780
|
if filter_name.startswith("_"):
|
|
786
781
|
del filters[filter_name]
|
|
787
782
|
|
|
788
|
-
if getattr(cls._meta.model, "is_contact_associable_model", False):
|
|
783
|
+
if getattr(cls._meta.model, "is_contact_associable_model", False):
|
|
789
784
|
# Add "contacts" and "teams" filters
|
|
790
785
|
from nautobot.extras.models import Contact, Team
|
|
791
786
|
|
|
@@ -805,13 +800,13 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
805
800
|
label="Teams (name or ID)",
|
|
806
801
|
)
|
|
807
802
|
|
|
808
|
-
if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False):
|
|
809
|
-
if not hasattr(cls._meta.model, "static_group_association_set"):
|
|
803
|
+
if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False):
|
|
804
|
+
if not hasattr(cls._meta.model, "static_group_association_set"):
|
|
810
805
|
logger.warning(
|
|
811
806
|
"Model %s has 'is_dynamic_group_associable_model = True' but lacks "
|
|
812
807
|
"a 'static_group_association_set' attribute. Perhaps this is due to it inheriting from "
|
|
813
808
|
"the deprecated DynamicGroupMixin class instead of the preferred DynamicGroupsModelMixin?",
|
|
814
|
-
cls._meta.model,
|
|
809
|
+
cls._meta.model,
|
|
815
810
|
)
|
|
816
811
|
else:
|
|
817
812
|
# Add "dynamic_groups" field as the last key
|
|
@@ -821,14 +816,14 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
821
816
|
queryset=DynamicGroup.objects.all(),
|
|
822
817
|
field_name="static_group_association_set__dynamic_group",
|
|
823
818
|
to_field_name="name",
|
|
824
|
-
query_params={"content_type": cls._meta.model._meta.label_lower},
|
|
819
|
+
query_params={"content_type": cls._meta.model._meta.label_lower},
|
|
825
820
|
label="Dynamic groups (name or ID)",
|
|
826
821
|
)
|
|
827
822
|
|
|
828
823
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
829
|
-
if cls._meta.model is not None:
|
|
824
|
+
if cls._meta.model is not None:
|
|
830
825
|
if "tags" in filters and isinstance(filters["tags"], TagFilter):
|
|
831
|
-
filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]}
|
|
826
|
+
filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]}
|
|
832
827
|
|
|
833
828
|
new_filters = {}
|
|
834
829
|
for existing_filter_name, existing_filter in filters.items():
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"model": "users.user",
|
|
4
|
+
"pk": "90798182-9d19-479a-899f-a415fc2c62fe",
|
|
5
|
+
"fields": {
|
|
6
|
+
"password": "pbkdf2_sha256$216000$TtNpNWboQq6C$6vh6qqRXUGSuLZlwvvGRbz35BS6avrdEFudnjJv2HXY=",
|
|
7
|
+
"last_login": null,
|
|
8
|
+
"is_superuser": false,
|
|
9
|
+
"username": "alice",
|
|
10
|
+
"first_name": "",
|
|
11
|
+
"last_name": "",
|
|
12
|
+
"email": "alice@localhost",
|
|
13
|
+
"is_staff": false,
|
|
14
|
+
"is_active": true,
|
|
15
|
+
"date_joined": "2021-05-25T23:49:55.008Z",
|
|
16
|
+
"config_data": {},
|
|
17
|
+
"groups": [],
|
|
18
|
+
"user_permissions": []
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"model": "users.user",
|
|
23
|
+
"pk": "90798182-9d19-479a-899f-a415fc2c62fd",
|
|
24
|
+
"fields": {
|
|
25
|
+
"password": "pbkdf2_sha256$216000$TtNpNWboQq6C$6vh6qqRXUGSuLZlwvvGRbz35BS6avrdEFudnjJv2HXY=",
|
|
26
|
+
"last_login": null,
|
|
27
|
+
"is_superuser": true,
|
|
28
|
+
"username": "bob",
|
|
29
|
+
"first_name": "",
|
|
30
|
+
"last_name": "",
|
|
31
|
+
"email": "bob@localhost",
|
|
32
|
+
"is_staff": true,
|
|
33
|
+
"is_active": true,
|
|
34
|
+
"date_joined": "2021-05-25T23:49:55.008Z",
|
|
35
|
+
"config_data": {},
|
|
36
|
+
"groups": [],
|
|
37
|
+
"user_permissions": []
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"model": "users.user",
|
|
42
|
+
"pk": "90798182-9d19-479a-899f-a415fc2c62ff",
|
|
43
|
+
"fields": {
|
|
44
|
+
"password": "pbkdf2_sha256$216000$TtNpNWboQq6C$6vh6qqRXUGSuLZlwvvGRbz35BS6avrdEFudnjJv2HXY=",
|
|
45
|
+
"last_login": null,
|
|
46
|
+
"is_superuser": false,
|
|
47
|
+
"username": "charlie",
|
|
48
|
+
"first_name": "",
|
|
49
|
+
"last_name": "",
|
|
50
|
+
"email": "charlie@localhost",
|
|
51
|
+
"is_staff": false,
|
|
52
|
+
"is_active": true,
|
|
53
|
+
"date_joined": "2021-05-25T23:49:55.008Z",
|
|
54
|
+
"config_data": {},
|
|
55
|
+
"groups": [],
|
|
56
|
+
"user_permissions": []
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|