nautobot 2.3.16__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/__init__.py +15 -0
- nautobot/apps/api.py +0 -2
- nautobot/apps/config.py +32 -3
- nautobot/apps/events.py +19 -0
- nautobot/apps/exceptions.py +0 -2
- nautobot/apps/ui.py +44 -9
- nautobot/apps/utils.py +0 -8
- nautobot/apps/views.py +2 -0
- nautobot/circuits/navigation.py +0 -57
- nautobot/circuits/tables.py +1 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
- nautobot/circuits/tests/integration/test_relationships.py +33 -24
- nautobot/circuits/tests/test_filters.py +4 -8
- nautobot/circuits/views.py +143 -26
- nautobot/cloud/factory.py +4 -1
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/tests/test_filters.py +5 -4
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/metadata.py +28 -256
- nautobot/core/api/pagination.py +3 -2
- nautobot/core/api/renderers.py +3 -0
- nautobot/core/api/serializers.py +24 -244
- nautobot/core/api/urls.py +3 -4
- nautobot/core/api/utils.py +0 -62
- nautobot/core/api/views.py +48 -158
- nautobot/core/apps/__init__.py +22 -578
- nautobot/core/celery/__init__.py +13 -0
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +48 -3
- nautobot/core/cli/__init__.py +8 -0
- nautobot/core/constants.py +7 -0
- nautobot/core/events/__init__.py +116 -0
- nautobot/core/events/base.py +27 -0
- nautobot/core/events/exceptions.py +10 -0
- nautobot/core/events/redis_broker.py +48 -0
- nautobot/core/events/syslog_broker.py +19 -0
- nautobot/core/exceptions.py +0 -6
- nautobot/core/filters.py +16 -21
- nautobot/core/fixtures/user-data.json +59 -0
- nautobot/core/forms/fields.py +53 -8
- nautobot/core/forms/utils.py +2 -1
- nautobot/core/graphql/schema.py +3 -1
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +4 -4
- nautobot/core/jobs/cleanup.py +13 -49
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/generate_test_data.py +21 -0
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/middleware.py +16 -0
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/fields.py +11 -7
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +3 -6
- nautobot/core/settings.py +44 -7
- nautobot/core/settings.yaml +86 -8
- nautobot/core/tables.py +15 -65
- nautobot/core/tasks.py +1 -1
- nautobot/core/templates/components/button/default.html +7 -0
- nautobot/core/templates/components/button/dropdown.html +20 -0
- nautobot/core/templates/components/layout/one_over_two.html +19 -0
- nautobot/core/templates/components/layout/two_over_one.html +19 -0
- nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
- nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
- nautobot/core/templates/components/panel/body_content_tags.html +6 -0
- nautobot/core/templates/components/panel/body_content_text.html +12 -0
- nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
- nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
- nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
- nautobot/core/templates/components/panel/footer_content_table.html +14 -0
- nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
- nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
- nautobot/core/templates/components/panel/panel.html +16 -0
- nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
- nautobot/core/templates/components/tab/content_wrapper.html +3 -0
- nautobot/core/templates/components/tab/label_wrapper.html +5 -0
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
- nautobot/core/templates/generic/object_retrieve.html +28 -17
- nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
- nautobot/core/templates/inc/custom_fields/panel.html +2 -2
- nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
- nautobot/core/templates/inc/footer.html +1 -0
- nautobot/core/templates/inc/media.html +0 -3
- nautobot/core/templates/inc/nav_menu.html +1 -1
- nautobot/core/templates/inc/relationships_panel.html +1 -1
- nautobot/core/templates/nautobot_config.py.j2 +3 -3
- nautobot/core/templates/panel_table.html +12 -0
- nautobot/core/templates/search.html +0 -7
- nautobot/core/templates/utilities/render_jinja2.html +117 -0
- nautobot/core/templatetags/helpers.py +101 -12
- nautobot/core/templatetags/ui_framework.py +40 -0
- nautobot/core/testing/api.py +23 -128
- nautobot/core/testing/context.py +18 -0
- nautobot/core/testing/filters.py +41 -58
- nautobot/core/testing/mixins.py +2 -7
- nautobot/core/testing/views.py +25 -123
- 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 +1 -2
- nautobot/core/tests/nautobot_config.py +198 -0
- nautobot/core/tests/runner.py +3 -3
- nautobot/core/tests/test_api.py +82 -201
- nautobot/core/tests/test_csv.py +3 -25
- nautobot/core/tests/test_events.py +214 -0
- nautobot/core/tests/test_jinja_filters.py +1 -0
- nautobot/core/tests/test_jobs.py +84 -13
- nautobot/core/tests/test_navigations.py +7 -241
- nautobot/core/tests/test_templatetags_helpers.py +16 -0
- nautobot/core/tests/test_ui.py +150 -0
- nautobot/core/tests/test_utils.py +0 -25
- nautobot/core/tests/test_views.py +123 -31
- nautobot/core/ui/__init__.py +0 -0
- nautobot/core/ui/base.py +11 -0
- nautobot/core/ui/choices.py +44 -0
- nautobot/core/ui/homepage.py +167 -0
- nautobot/core/ui/nav.py +279 -0
- nautobot/core/ui/object_detail.py +1841 -0
- nautobot/core/ui/utils.py +36 -0
- nautobot/core/urls.py +4 -9
- nautobot/core/utils/config.py +30 -3
- nautobot/core/utils/lookup.py +20 -13
- nautobot/core/views/__init__.py +6 -1
- nautobot/core/views/generic.py +47 -52
- nautobot/core/views/mixins.py +15 -25
- nautobot/core/views/paginator.py +8 -5
- nautobot/core/views/renderers.py +3 -3
- nautobot/core/views/utils.py +11 -0
- nautobot/core/wsgi.py +3 -3
- nautobot/dcim/api/serializers.py +80 -179
- nautobot/dcim/api/urls.py +5 -0
- nautobot/dcim/api/views.py +17 -4
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/choices.py +28 -0
- nautobot/dcim/factory.py +58 -0
- nautobot/dcim/filters/__init__.py +197 -24
- nautobot/dcim/forms.py +203 -12
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
- nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
- nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
- nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
- nautobot/dcim/models/__init__.py +4 -0
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +20 -22
- nautobot/dcim/models/devices.py +173 -4
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +5 -6
- nautobot/dcim/models/racks.py +6 -6
- nautobot/dcim/navigation.py +25 -224
- nautobot/dcim/signals.py +44 -0
- nautobot/dcim/tables/__init__.py +5 -3
- nautobot/dcim/tables/devices.py +96 -2
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/templates/dcim/controller/base.html +10 -0
- nautobot/dcim/templates/dcim/controller_create.html +1 -0
- nautobot/dcim/templates/dcim/controller_retrieve.html +5 -1
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +66 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +46 -0
- nautobot/dcim/templates/dcim/device/base.html +6 -42
- nautobot/dcim/templates/dcim/device/wireless.html +73 -0
- nautobot/dcim/templates/dcim/device.html +3 -1
- nautobot/dcim/templates/dcim/interface.html +1 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/locationtype.html +0 -107
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +76 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +34 -0
- nautobot/dcim/tests/test_api.py +172 -61
- nautobot/dcim/tests/test_filters.py +171 -109
- nautobot/dcim/tests/test_forms.py +2 -51
- nautobot/dcim/tests/test_graphql.py +0 -52
- nautobot/dcim/tests/test_models.py +126 -4
- nautobot/dcim/tests/test_signals.py +1 -0
- nautobot/dcim/tests/test_views.py +103 -11
- nautobot/dcim/urls.py +72 -27
- nautobot/dcim/utils.py +2 -2
- nautobot/dcim/views.py +369 -62
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +91 -75
- nautobot/extras/api/urls.py +4 -0
- nautobot/extras/api/views.py +78 -15
- nautobot/extras/choices.py +13 -0
- nautobot/extras/constants.py +0 -1
- nautobot/extras/context_managers.py +23 -6
- nautobot/extras/datasources/git.py +4 -1
- nautobot/extras/factory.py +27 -0
- nautobot/extras/filters/__init__.py +59 -0
- nautobot/extras/forms/forms.py +125 -30
- nautobot/extras/forms/mixins.py +3 -11
- nautobot/extras/graphql/types.py +25 -1
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +2 -1
- nautobot/extras/jobs.py +62 -26
- nautobot/extras/management/__init__.py +1 -0
- nautobot/extras/management/commands/runjob.py +7 -79
- nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
- nautobot/extras/management/utils.py +87 -0
- nautobot/extras/managers.py +1 -3
- nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
- nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
- nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
- nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
- nautobot/extras/models/__init__.py +4 -0
- nautobot/extras/models/change_logging.py +7 -3
- nautobot/extras/models/customfields.py +11 -12
- nautobot/extras/models/groups.py +9 -13
- nautobot/extras/models/jobs.py +218 -37
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/models/relationships.py +69 -1
- nautobot/extras/models/secrets.py +5 -0
- nautobot/extras/navigation.py +20 -262
- nautobot/extras/plugins/__init__.py +56 -32
- nautobot/extras/plugins/marketplace_manifest.yml +450 -0
- nautobot/extras/plugins/urls.py +1 -0
- nautobot/extras/plugins/views.py +48 -1
- nautobot/extras/signals.py +39 -1
- nautobot/extras/tables.py +40 -6
- nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
- nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
- nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
- nautobot/extras/templates/extras/job_detail.html +36 -6
- nautobot/extras/templates/extras/job_edit.html +5 -2
- nautobot/extras/templates/extras/job_list.html +2 -7
- nautobot/extras/templates/extras/jobqueue_retrieve.html +44 -0
- nautobot/extras/templates/extras/marketplace.html +278 -0
- nautobot/extras/templates/extras/plugins_list.html +35 -1
- nautobot/extras/templates/extras/plugins_tiles.html +79 -0
- nautobot/extras/templates/extras/role_retrieve.html +16 -0
- nautobot/extras/templates/extras/secret.html +0 -65
- nautobot/extras/templates/extras/secret_check.js +16 -0
- nautobot/extras/templates/extras/secret_create.html +114 -0
- nautobot/extras/templates/extras/secret_edit.html +1 -114
- nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
- nautobot/extras/templatetags/job_buttons.py +5 -4
- nautobot/extras/templatetags/plugins.py +69 -6
- 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 +157 -55
- nautobot/extras/tests/test_context_managers.py +4 -1
- 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 +219 -535
- nautobot/extras/tests/test_forms.py +1 -20
- nautobot/extras/tests/test_job_variables.py +73 -152
- nautobot/extras/tests/test_jobs.py +43 -54
- nautobot/extras/tests/test_models.py +71 -16
- nautobot/extras/tests/test_relationships.py +5 -2
- nautobot/extras/tests/test_utils.py +23 -2
- nautobot/extras/tests/test_views.py +183 -43
- nautobot/extras/tests/test_webhooks.py +2 -1
- nautobot/extras/urls.py +2 -20
- nautobot/extras/utils.py +118 -4
- nautobot/extras/views.py +203 -92
- nautobot/extras/webhooks.py +5 -2
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +36 -137
- nautobot/ipam/api/views.py +93 -62
- nautobot/ipam/lookups.py +62 -101
- nautobot/ipam/models.py +11 -63
- nautobot/ipam/navigation.py +0 -90
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +6 -20
- nautobot/ipam/templates/ipam/routetarget.html +0 -28
- nautobot/ipam/templates/ipam/vrf.html +0 -47
- nautobot/ipam/tests/test_api.py +8 -419
- nautobot/ipam/tests/test_filters.py +39 -119
- nautobot/ipam/tests/test_forms.py +47 -51
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +0 -41
- nautobot/ipam/tests/test_querysets.py +1 -63
- nautobot/ipam/urls.py +3 -69
- nautobot/ipam/utils/__init__.py +0 -24
- nautobot/ipam/views.py +153 -198
- nautobot/project-static/css/base.css +38 -3
- nautobot/project-static/docs/404.html +421 -19
- nautobot/project-static/docs/apps/index.html +421 -19
- nautobot/project-static/docs/apps/nautobot-apps.html +421 -19
- nautobot/project-static/docs/assets/extra.css +5 -1
- 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 +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +421 -172
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +425 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +424 -22
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9809 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +424 -63
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +457 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +425 -25
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +457 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +425 -215
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +430 -342
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5799 -1054
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +447 -176
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +460 -21
- nautobot/project-static/docs/development/apps/api/configuration-view.html +421 -19
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/global-search.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/graphql.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +424 -41
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +421 -19
- nautobot/project-static/docs/development/apps/api/prometheus.html +421 -19
- nautobot/project-static/docs/development/apps/api/setup.html +425 -155
- nautobot/project-static/docs/development/apps/api/testing.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +701 -130
- nautobot/project-static/docs/development/apps/api/views/base-template.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/index.html +423 -20
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +425 -19
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +451 -19
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/notes.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/urls.html +421 -19
- nautobot/project-static/docs/development/apps/index.html +421 -19
- nautobot/project-static/docs/development/apps/migration/code-updates.html +422 -52
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +422 -20
- nautobot/project-static/docs/development/apps/migration/from-v1.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +424 -22
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9219 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9333 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9474 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9517 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +424 -22
- nautobot/project-static/docs/development/core/application-registry.html +421 -19
- nautobot/project-static/docs/development/core/best-practices.html +421 -19
- nautobot/project-static/docs/development/core/bootstrap-ui.html +421 -19
- nautobot/project-static/docs/development/core/caching.html +421 -19
- nautobot/project-static/docs/development/core/controllers.html +423 -19
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +490 -45
- nautobot/project-static/docs/development/core/generic-views.html +421 -19
- nautobot/project-static/docs/development/core/getting-started.html +566 -179
- nautobot/project-static/docs/development/core/homepage.html +432 -30
- nautobot/project-static/docs/development/core/index.html +421 -19
- nautobot/project-static/docs/development/core/local-k8s.html +9453 -0
- nautobot/project-static/docs/development/core/model-checklist.html +424 -22
- nautobot/project-static/docs/development/core/model-features.html +421 -19
- nautobot/project-static/docs/development/core/natural-keys.html +421 -19
- nautobot/project-static/docs/development/core/navigation-menu.html +438 -26
- nautobot/project-static/docs/development/core/release-checklist.html +435 -45
- nautobot/project-static/docs/development/core/role-internals.html +421 -19
- nautobot/project-static/docs/development/core/settings.html +421 -19
- nautobot/project-static/docs/development/core/style-guide.html +421 -19
- nautobot/project-static/docs/development/core/templates.html +431 -22
- nautobot/project-static/docs/development/core/testing.html +421 -19
- nautobot/project-static/docs/development/core/ui-component-framework.html +11020 -0
- nautobot/project-static/docs/development/core/user-preferences.html +424 -22
- nautobot/project-static/docs/development/index.html +421 -19
- nautobot/project-static/docs/development/jobs/index.html +546 -160
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +421 -19
- nautobot/project-static/docs/index.html +421 -19
- nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
- nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
- nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
- nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
- nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
- nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
- nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
- nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +426 -20
- nautobot/project-static/docs/overview/design_philosophy.html +421 -19
- nautobot/project-static/docs/release-notes/index.html +445 -22
- nautobot/project-static/docs/release-notes/version-1.0.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.1.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.2.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.3.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.4.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.5.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.6.html +634 -667
- nautobot/project-static/docs/release-notes/version-2.0.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.1.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.2.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.3.html +684 -886
- nautobot/project-static/docs/release-notes/version-2.4.html +10007 -0
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +334 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +423 -21
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +433 -32
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +765 -180
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +434 -29
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/index.html +426 -20
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/services.html +421 -19
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +421 -19
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +442 -41
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +435 -66
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +435 -66
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +427 -21
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +457 -20
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +447 -22
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9333 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +424 -22
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +424 -22
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9271 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9175 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9169 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9235 -0
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +421 -19
- nautobot/project-static/docs/user-guide/index.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +9575 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +426 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9182 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9250 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +489 -56
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +421 -19
- nautobot/project-static/img/jinja_logo.svg +97 -0
- nautobot/project-static/js/forms.js +5 -0
- nautobot/project-static/js/nav_menu.js +2 -1
- nautobot/tenancy/api/serializers.py +0 -2
- nautobot/tenancy/factory.py +1 -1
- nautobot/tenancy/navigation.py +0 -29
- nautobot/tenancy/templates/tenancy/tenant.html +4 -91
- nautobot/tenancy/tests/test_filters.py +29 -134
- nautobot/tenancy/views.py +32 -23
- nautobot/users/admin.py +3 -1
- nautobot/users/api/serializers.py +4 -5
- nautobot/users/api/views.py +1 -1
- nautobot/users/forms.py +19 -0
- nautobot/users/templates/users/preferences.html +22 -0
- nautobot/users/tests/test_filters.py +1 -19
- nautobot/users/tests/test_views.py +57 -0
- nautobot/users/utils.py +8 -0
- nautobot/users/views.py +48 -11
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/filters.py +2 -20
- nautobot/virtualization/navigation.py +0 -48
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
- nautobot/virtualization/tests/test_filters.py +57 -183
- nautobot/virtualization/views.py +18 -15
- nautobot/wireless/__init__.py +0 -0
- nautobot/wireless/api/__init__.py +0 -0
- nautobot/wireless/api/serializers.py +44 -0
- nautobot/wireless/api/urls.py +20 -0
- nautobot/wireless/api/views.py +34 -0
- nautobot/wireless/apps.py +8 -0
- nautobot/wireless/choices.py +345 -0
- nautobot/wireless/factory.py +138 -0
- nautobot/wireless/filters.py +167 -0
- nautobot/wireless/forms.py +283 -0
- nautobot/wireless/homepage.py +19 -0
- nautobot/wireless/migrations/0001_initial.py +223 -0
- nautobot/wireless/migrations/__init__.py +0 -0
- nautobot/wireless/models.py +207 -0
- nautobot/wireless/navigation.py +105 -0
- nautobot/wireless/tables.py +244 -0
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +64 -0
- nautobot/wireless/tests/__init__.py +0 -0
- nautobot/wireless/tests/test_api.py +247 -0
- nautobot/wireless/tests/test_filters.py +54 -0
- nautobot/wireless/tests/test_models.py +22 -0
- nautobot/wireless/tests/test_views.py +378 -0
- nautobot/wireless/urls.py +13 -0
- nautobot/wireless/views.py +129 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +11 -14
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +674 -551
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
- nautobot/core/utils/navigation.py +0 -54
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/core/api/metadata.py
CHANGED
|
@@ -1,202 +1,25 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
from typing import Any, Dict, List
|
|
3
|
-
|
|
4
1
|
from django.core.exceptions import PermissionDenied
|
|
5
2
|
from django.http import Http404
|
|
6
|
-
from django.
|
|
7
|
-
from
|
|
8
|
-
import django_filters
|
|
9
|
-
import drf_react_template.schema_form_encoder as schema
|
|
10
|
-
from rest_framework import exceptions, fields as drf_fields, serializers as drf_serializers
|
|
3
|
+
from django.utils.encoding import force_str
|
|
4
|
+
from rest_framework import exceptions
|
|
11
5
|
from rest_framework.metadata import SimpleMetadata
|
|
12
6
|
from rest_framework.request import clone_request
|
|
13
7
|
|
|
14
|
-
from nautobot.core.
|
|
15
|
-
from nautobot.core.utils.lookup import get_route_for_model
|
|
16
|
-
|
|
17
|
-
# FIXME(jathan): I hate this pattern that these fields are hard-coded here. But for the moment, this
|
|
18
|
-
# works reliably.
|
|
19
|
-
BOTTOM_FIELDS = ["computed_fields", "custom_fields", "relationships"]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class NautobotProcessingMixin(schema.ProcessingMixin):
|
|
23
|
-
"""Processing mixin to account for custom field types and behaviors for Nautobot."""
|
|
24
|
-
|
|
25
|
-
def _get_type_map_value(self, field: schema.SerializerType):
|
|
26
|
-
"""Overload default to add "required" as a default mapping."""
|
|
27
|
-
# This adds "required" as a default type mapping compared to drf_react_template core.
|
|
28
|
-
result = {
|
|
29
|
-
"type": field.style.get("schema:type"),
|
|
30
|
-
"enum": field.style.get("schema:enum"),
|
|
31
|
-
"format": field.style.get("schema:format"),
|
|
32
|
-
"widget": field.style.get("ui:widget"),
|
|
33
|
-
"required": field.style.get("schema:required"),
|
|
34
|
-
"readOnly": field.style.get("schema:readOnly"),
|
|
35
|
-
}
|
|
36
|
-
result_default = self.TYPE_MAP.get(type(field).__name__, {})
|
|
37
|
-
for k in result_default:
|
|
38
|
-
if result[k] is None:
|
|
39
|
-
result[k] = result_default[k]
|
|
40
|
-
return result
|
|
41
|
-
|
|
42
|
-
def order_fields(self, fields):
|
|
43
|
-
"""Explicitly order the "big ugly" fields to the bottom."""
|
|
44
|
-
# FIXME(jathan): Correct the behavior introduced in #3500 by switching to `__all__` to
|
|
45
|
-
# assert these get added at the end.
|
|
46
|
-
for field_name in BOTTOM_FIELDS:
|
|
47
|
-
if field_name in fields:
|
|
48
|
-
fields.remove(field_name)
|
|
49
|
-
fields.append(field_name)
|
|
50
|
-
return fields
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class NautobotSchemaProcessor(NautobotProcessingMixin, schema.SchemaProcessor):
|
|
54
|
-
"""SchemaProcessor to account for custom field types and behaviors for Nautobot."""
|
|
55
|
-
|
|
56
|
-
def _get_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
|
|
57
|
-
"""
|
|
58
|
-
This method is used to generate the proper schema based on serializer field mappings or
|
|
59
|
-
per-field attribute markup.
|
|
60
|
-
|
|
61
|
-
This has been overloaded with an `elif` to account for `ManyRelatedField`.
|
|
62
|
-
"""
|
|
63
|
-
type_map_obj = self._get_type_map_value(field)
|
|
64
|
-
result = {
|
|
65
|
-
"type": type_map_obj["type"] or "string",
|
|
66
|
-
"title": self._get_title(field, name),
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if isinstance(field, drf_serializers.ListField):
|
|
70
|
-
if field.allow_empty:
|
|
71
|
-
result["required"] = not getattr(field, "allow_empty", True)
|
|
72
|
-
result["items"] = self._get_field_properties(field.child, "")
|
|
73
|
-
result["uniqueItems"] = True
|
|
74
|
-
elif isinstance(field, drf_serializers.ManyRelatedField):
|
|
75
|
-
if field.allow_empty:
|
|
76
|
-
result["required"] = type_map_obj.get("required", [])
|
|
77
|
-
result["items"] = self._get_field_properties(field.child_relation, "")
|
|
78
|
-
result["uniqueItems"] = True
|
|
79
|
-
else:
|
|
80
|
-
if isinstance(field, drf_serializers.RelatedField):
|
|
81
|
-
result["uniqueItems"] = True
|
|
82
|
-
if hasattr(field, "queryset") and hasattr(field.queryset, "model"):
|
|
83
|
-
# We construct the correct list view url here so we can just append the object id to the end of the url in our frontend.
|
|
84
|
-
# e.g. /dcim/locations/
|
|
85
|
-
try:
|
|
86
|
-
model_url = reverse(get_route_for_model(model=field.queryset.model, action="list"))
|
|
87
|
-
except NoReverseMatch:
|
|
88
|
-
model_url = None
|
|
89
|
-
model_options = field.queryset.model._meta
|
|
90
|
-
result["required"] = field.required
|
|
91
|
-
# Custom Keyword: modelName, modelNamePlural and appLabel
|
|
92
|
-
# modelName represents the model name of the uuid model
|
|
93
|
-
# modelUrl represents the UI URL to list instances of this model
|
|
94
|
-
# and appLabel represents the app_name of the model
|
|
95
|
-
result["modelName"] = model_options.model_name
|
|
96
|
-
result["appLabel"] = model_options.app_label
|
|
97
|
-
result["modelUrl"] = model_url
|
|
98
|
-
if field.allow_null:
|
|
99
|
-
result["type"] = [result["type"], "null"]
|
|
100
|
-
if enum := type_map_obj.get("enum"):
|
|
101
|
-
if enum == "choices":
|
|
102
|
-
choices = field.choices
|
|
103
|
-
result["enum"] = list(choices.keys())
|
|
104
|
-
result["enumNames"] = list(choices.values())
|
|
105
|
-
if isinstance(enum, (list, tuple)):
|
|
106
|
-
result["enum"] = [item[0] for item in enum]
|
|
107
|
-
result["enumNames"] = [item[1] for item in enum]
|
|
108
|
-
if format_ := type_map_obj["format"]:
|
|
109
|
-
result["format"] = format_
|
|
110
|
-
|
|
111
|
-
if read_only := field.read_only:
|
|
112
|
-
result["readOnly"] = read_only
|
|
113
|
-
|
|
114
|
-
with contextlib.suppress(drf_fields.SkipField):
|
|
115
|
-
result["default"] = field.get_default()
|
|
116
|
-
|
|
117
|
-
result = self._set_validation_properties(field, result)
|
|
118
|
-
|
|
119
|
-
return result
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def _filter_fields(all_fields):
|
|
123
|
-
"""
|
|
124
|
-
Override super._filter_fields to return all fields, including read-only fields,
|
|
125
|
-
as read-only fields have to be displayed in Detail View.
|
|
126
|
-
"""
|
|
127
|
-
return tuple((name, field) for name, field in all_fields)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class NautobotUiSchemaProcessor(NautobotProcessingMixin, schema.UiSchemaProcessor):
|
|
131
|
-
"""UiSchemaProcessor to account for custom field types and behaviors for Nautobot."""
|
|
132
|
-
|
|
133
|
-
def _field_order(self) -> List[str]:
|
|
134
|
-
"""
|
|
135
|
-
Overload the base which just returns `Meta.fields` and doesn't play nicely with "__all__".
|
|
136
|
-
|
|
137
|
-
This instead calls `get_fields()` and returns the keys.
|
|
138
|
-
"""
|
|
139
|
-
if self._is_list_serializer(self.serializer):
|
|
140
|
-
fields = self.serializer.child.get_fields()
|
|
141
|
-
else:
|
|
142
|
-
fields = self.serializer.get_fields()
|
|
143
|
-
|
|
144
|
-
field_names = self.order_fields(list(fields))
|
|
145
|
-
|
|
146
|
-
return field_names
|
|
147
|
-
|
|
148
|
-
def _get_ui_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
|
|
149
|
-
"""
|
|
150
|
-
We had to overload this here to make it so that array types with children validate properly
|
|
151
|
-
and to also use `NautobotUiSchemaProcessor` over the default.
|
|
152
|
-
"""
|
|
153
|
-
data_index = self._generate_data_index(name)
|
|
154
|
-
result = {}
|
|
155
|
-
is_int = False
|
|
156
|
-
is_list = False
|
|
157
|
-
if self._is_field_serializer(field):
|
|
158
|
-
return NautobotUiSchemaProcessor(field, self.renderer_context, prefix=data_index).get_ui_schema()
|
|
159
|
-
elif isinstance(field, drf_serializers.ListField):
|
|
160
|
-
is_list = True
|
|
161
|
-
child = field.child
|
|
162
|
-
is_int = isinstance(child, drf_serializers.IntegerField)
|
|
163
|
-
widget = self._get_type_map_value(field=child).get("widget")
|
|
164
|
-
if not widget and isinstance(child, drf_serializers.ChoiceField):
|
|
165
|
-
widget = "checkbox"
|
|
166
|
-
else:
|
|
167
|
-
widget = self._get_type_map_value(field=field).get("widget")
|
|
168
|
-
help_text = field.help_text
|
|
169
|
-
if widget:
|
|
170
|
-
if is_list and is_int:
|
|
171
|
-
if "items" not in result:
|
|
172
|
-
result["items"] = {}
|
|
173
|
-
result["items"]["ui:widget"] = widget
|
|
174
|
-
else:
|
|
175
|
-
result["ui:widget"] = widget
|
|
176
|
-
if help_text:
|
|
177
|
-
result["ui:help"] = help_text
|
|
178
|
-
result.update(self._get_style_dict(field))
|
|
179
|
-
result = self._set_validation_properties(field, result)
|
|
180
|
-
return result
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
class NautobotColumnProcessor(NautobotProcessingMixin, schema.ColumnProcessor):
|
|
184
|
-
"""ColumnProcessor to account for custom field types and behaviors for Nautobot."""
|
|
8
|
+
from nautobot.core.api import ContentTypeField
|
|
185
9
|
|
|
186
10
|
|
|
187
11
|
class NautobotMetadata(SimpleMetadata):
|
|
188
12
|
"""
|
|
189
|
-
Metadata class that emits JSON schema.
|
|
190
|
-
|
|
191
|
-
- schema: The object JSON schema
|
|
192
|
-
- uiSchema: The object UI schema which describes the form layout in the UI
|
|
13
|
+
Metadata class that emits JSON schema.
|
|
193
14
|
"""
|
|
194
15
|
|
|
195
16
|
def determine_actions(self, request, view):
|
|
196
17
|
"""Generate the actions and return the names of the allowed methods."""
|
|
197
|
-
actions =
|
|
18
|
+
actions = {}
|
|
198
19
|
for method in {"PUT", "POST"} & set(view.allowed_methods):
|
|
199
20
|
view.request = clone_request(request, method)
|
|
21
|
+
view.request.major_version = request.major_version
|
|
22
|
+
view.request.minor_version = request.minor_version
|
|
200
23
|
try:
|
|
201
24
|
# Test global permissions
|
|
202
25
|
if hasattr(view, "check_permissions"):
|
|
@@ -207,82 +30,31 @@ class NautobotMetadata(SimpleMetadata):
|
|
|
207
30
|
except (exceptions.APIException, PermissionDenied, Http404):
|
|
208
31
|
pass
|
|
209
32
|
else:
|
|
210
|
-
|
|
33
|
+
# If user has appropriate permissions for the view, include
|
|
34
|
+
# appropriate metadata about the fields that should be supplied.
|
|
35
|
+
serializer = view.get_serializer()
|
|
36
|
+
actions[method] = self.get_serializer_info(serializer)
|
|
211
37
|
finally:
|
|
212
38
|
view.request = request
|
|
213
39
|
return actions
|
|
214
40
|
|
|
215
|
-
def
|
|
216
|
-
"""
|
|
217
|
-
|
|
218
|
-
return list(getattr(serializer_meta, "list_display_fields", []))
|
|
219
|
-
|
|
220
|
-
def determine_metadata(self, request, view):
|
|
221
|
-
"""This is the metadata that gets returned on an `OPTIONS` request."""
|
|
222
|
-
metadata = super().determine_metadata(request, view)
|
|
223
|
-
|
|
224
|
-
# Include the object type label for this model.
|
|
225
|
-
object_type = view.queryset.model._meta.label_lower if getattr(view, "queryset", None) else "unknown"
|
|
226
|
-
metadata["object_type"] = object_type
|
|
41
|
+
def get_field_info(self, field):
|
|
42
|
+
"""
|
|
43
|
+
Fixup field information:
|
|
227
44
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
ui_schema = NautobotUiSchemaProcessor(serializer, request.parser_context).get_ui_schema()
|
|
237
|
-
ui_schema["ui:order"] = [*self.get_list_display_fields(serializer), "*"]
|
|
238
|
-
metadata.update(
|
|
45
|
+
- Set choices for ContentTypeField
|
|
46
|
+
- Replace DRF choices `display_name` to `display` to match new pattern.
|
|
47
|
+
"""
|
|
48
|
+
field_info = super().get_field_info(field)
|
|
49
|
+
for choice in field_info.get("choices", []):
|
|
50
|
+
choice["display"] = choice.pop("display_name")
|
|
51
|
+
if hasattr(field, "queryset") and not field_info.get("read_only") and isinstance(field, ContentTypeField):
|
|
52
|
+
field_info["choices"] = [
|
|
239
53
|
{
|
|
240
|
-
"
|
|
241
|
-
"
|
|
54
|
+
"value": choice_value,
|
|
55
|
+
"display": force_str(choice_name, strings_only=True),
|
|
242
56
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if hasattr(serializer, "determine_view_options"):
|
|
248
|
-
metadata["view_options"] = serializer.determine_view_options(request)
|
|
249
|
-
|
|
250
|
-
return metadata
|
|
251
|
-
|
|
252
|
-
def get_filter_info(self, view):
|
|
253
|
-
"""Enumerate filterset information for the view. Returns a dictionary with the following format:
|
|
254
|
-
|
|
255
|
-
{
|
|
256
|
-
"filter_name": {
|
|
257
|
-
"label": "Filter Label",
|
|
258
|
-
"lookup_types": [
|
|
259
|
-
{"value": "filter_name__n", "label": "not exact (n)"},
|
|
260
|
-
{"value": "filter_name__re", "label": "matches regex (re)"},
|
|
261
|
-
...
|
|
262
|
-
]
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
if not getattr(view, "filterset_class", None):
|
|
269
|
-
return {}
|
|
270
|
-
filterset = view.filterset_class
|
|
271
|
-
filters = {}
|
|
272
|
-
for filter_name, filter_instance in sorted(
|
|
273
|
-
filterset.base_filters.items(),
|
|
274
|
-
key=lambda x: get_filter_field_label(x[1]),
|
|
275
|
-
):
|
|
276
|
-
filter_key = filter_name.rsplit("__", 1)[0]
|
|
277
|
-
label = get_filter_field_label(filter_instance)
|
|
278
|
-
lookup_label = self._filter_lookup_label(filter_name, filter_instance)
|
|
279
|
-
filters.setdefault(filter_key, {"label": label})
|
|
280
|
-
filters[filter_key].setdefault("lookup_types", []).append({"value": filter_name, "label": lookup_label})
|
|
281
|
-
return filters
|
|
282
|
-
|
|
283
|
-
# TODO: move this into `build_lookup_label` when the legacy UI is removed
|
|
284
|
-
def _filter_lookup_label(self, filter_name, filter_instance):
|
|
285
|
-
"""Fix confusing lookup labels for boolean filters."""
|
|
286
|
-
if isinstance(filter_instance, django_filters.BooleanFilter):
|
|
287
|
-
return "exact"
|
|
288
|
-
return build_lookup_label(filter_name, filter_instance.lookup_expr)
|
|
57
|
+
for choice_value, choice_name in field.choices.items()
|
|
58
|
+
]
|
|
59
|
+
field_info["choices"].sort(key=lambda item: item["display"])
|
|
60
|
+
return field_info
|
nautobot/core/api/pagination.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from rest_framework.pagination import LimitOffsetPagination
|
|
2
2
|
|
|
3
|
+
from nautobot.core.constants import MAX_PAGE_SIZE_DEFAULT, PAGINATE_COUNT_DEFAULT
|
|
3
4
|
from nautobot.core.utils.config import get_settings_or_config
|
|
4
5
|
|
|
5
6
|
|
|
@@ -38,7 +39,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
|
|
|
38
39
|
if limit < 0:
|
|
39
40
|
raise ValueError()
|
|
40
41
|
# Enforce maximum page size, if defined
|
|
41
|
-
max_page_size = get_settings_or_config("MAX_PAGE_SIZE")
|
|
42
|
+
max_page_size = get_settings_or_config("MAX_PAGE_SIZE", fallback=MAX_PAGE_SIZE_DEFAULT)
|
|
42
43
|
if max_page_size:
|
|
43
44
|
if limit == 0:
|
|
44
45
|
return max_page_size
|
|
@@ -48,7 +49,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
|
|
|
48
49
|
except (KeyError, ValueError):
|
|
49
50
|
pass
|
|
50
51
|
|
|
51
|
-
return get_settings_or_config("PAGINATE_COUNT")
|
|
52
|
+
return get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
|
|
52
53
|
|
|
53
54
|
def get_next_link(self):
|
|
54
55
|
# Pagination has been disabled
|
nautobot/core/api/renderers.py
CHANGED
|
@@ -156,6 +156,9 @@ class NautobotCSVRenderer(BaseRenderer):
|
|
|
156
156
|
# value = [v["composite_key"] for v in value]
|
|
157
157
|
else:
|
|
158
158
|
value = [v["id"] for v in value]
|
|
159
|
+
if value and isinstance(value[0], dict) and ("value" in value[0]) and ("label" in value[0]):
|
|
160
|
+
# Multiple enum types
|
|
161
|
+
value = [v["value"] for v in value]
|
|
159
162
|
# The below makes for better UX than `json.dump()` for most current cases.
|
|
160
163
|
value = ",".join([str(v) if v is not None else "" for v in value])
|
|
161
164
|
elif not isinstance(value, (str, int)):
|
nautobot/core/api/serializers.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
-
from copy import deepcopy
|
|
3
2
|
import logging
|
|
4
3
|
import uuid
|
|
5
4
|
|
|
@@ -18,7 +17,7 @@ from django.db.models.functions import Cast
|
|
|
18
17
|
from django.urls import NoReverseMatch
|
|
19
18
|
from drf_spectacular.types import OpenApiTypes
|
|
20
19
|
from drf_spectacular.utils import extend_schema_field, PolymorphicProxySerializer as _PolymorphicProxySerializer
|
|
21
|
-
from rest_framework import
|
|
20
|
+
from rest_framework import serializers
|
|
22
21
|
from rest_framework.exceptions import PermissionDenied, ValidationError
|
|
23
22
|
from rest_framework.fields import CreateOnlyDefault
|
|
24
23
|
from rest_framework.reverse import reverse
|
|
@@ -31,11 +30,9 @@ from nautobot.core.api.utils import (
|
|
|
31
30
|
dict_to_filter_params,
|
|
32
31
|
nested_serializer_factory,
|
|
33
32
|
)
|
|
34
|
-
from nautobot.core.exceptions import ViewConfigException
|
|
35
33
|
from nautobot.core.models.fields import LaxURLField as LaxURLModelField
|
|
36
34
|
from nautobot.core.models.managers import TagsManager
|
|
37
35
|
from nautobot.core.models.utils import construct_composite_key, construct_natural_slug
|
|
38
|
-
from nautobot.core.templatetags.helpers import bettertitle
|
|
39
36
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
40
37
|
from nautobot.core.utils.requests import normalize_querydict
|
|
41
38
|
from nautobot.extras.api.customfields import CustomFieldDefaultValues, CustomFieldsDataField
|
|
@@ -134,7 +131,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
|
|
|
134
131
|
|
|
135
132
|
serializer_related_field = NautobotHyperlinkedRelatedField
|
|
136
133
|
|
|
137
|
-
id = serializers.UUIDField(read_only=False, default=serializers.CreateOnlyDefault(uuid.uuid4))
|
|
138
134
|
display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
|
|
139
135
|
object_type = ObjectTypeField()
|
|
140
136
|
# composite_key = serializers.SerializerMethodField() # TODO: Revisit if we reintroduce composite keys
|
|
@@ -297,21 +293,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
|
|
|
297
293
|
"""Return whether this is a nested serializer."""
|
|
298
294
|
return getattr(self.Meta, "is_nested", False)
|
|
299
295
|
|
|
300
|
-
@property
|
|
301
|
-
def list_display_fields(self):
|
|
302
|
-
return list(getattr(self.Meta, "list_display_fields", []))
|
|
303
|
-
|
|
304
|
-
@property
|
|
305
|
-
def advanced_tab_fields(self):
|
|
306
|
-
advanced_fields = list(
|
|
307
|
-
getattr(
|
|
308
|
-
self.Meta,
|
|
309
|
-
"advanced_tab_fields",
|
|
310
|
-
["id", "url", "object_type", "created", "last_updated", "natural_slug"],
|
|
311
|
-
)
|
|
312
|
-
)
|
|
313
|
-
return [field for field in advanced_fields if field in self.fields]
|
|
314
|
-
|
|
315
296
|
@extend_schema_field(serializers.CharField)
|
|
316
297
|
def get_display(self, instance):
|
|
317
298
|
"""
|
|
@@ -405,221 +386,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
|
|
|
405
386
|
fields = [field for field in fields if filter_field(field)]
|
|
406
387
|
return fields
|
|
407
388
|
|
|
408
|
-
def determine_view_options(self, request=None):
|
|
409
|
-
"""
|
|
410
|
-
Determine view options to use for rendering the list and detail views associated with this serializer.
|
|
411
|
-
"""
|
|
412
|
-
list_display = []
|
|
413
|
-
fields = []
|
|
414
|
-
|
|
415
|
-
from nautobot.core.api.metadata import NautobotColumnProcessor # avoid circular import
|
|
416
|
-
|
|
417
|
-
processor = NautobotColumnProcessor(self, request.parser_context if request else {})
|
|
418
|
-
field_map = dict(self.fields)
|
|
419
|
-
all_fields = list(field_map)
|
|
420
|
-
|
|
421
|
-
# Explicitly order the "big ugly" fields to the bottom
|
|
422
|
-
processor.order_fields(all_fields)
|
|
423
|
-
list_display_fields = self.list_display_fields
|
|
424
|
-
|
|
425
|
-
# Process the list_display_fields first.
|
|
426
|
-
for field_name in list_display_fields:
|
|
427
|
-
try:
|
|
428
|
-
field = field_map[field_name]
|
|
429
|
-
except KeyError:
|
|
430
|
-
continue # Ignore unknown fields.
|
|
431
|
-
column_data = processor._get_column_properties(field, field_name)
|
|
432
|
-
list_display.append(column_data)
|
|
433
|
-
fields.append(column_data)
|
|
434
|
-
|
|
435
|
-
# Process the rest of the fields second.
|
|
436
|
-
for field_name in all_fields:
|
|
437
|
-
# Don't process list display fields twice.
|
|
438
|
-
if field_name in list_display_fields:
|
|
439
|
-
continue
|
|
440
|
-
try:
|
|
441
|
-
field = field_map[field_name]
|
|
442
|
-
except KeyError:
|
|
443
|
-
continue # Ignore unknown fields.
|
|
444
|
-
column_data = processor._get_column_properties(field, field_name)
|
|
445
|
-
fields.append(column_data)
|
|
446
|
-
|
|
447
|
-
return {
|
|
448
|
-
"retrieve": {
|
|
449
|
-
"tabs": self._determine_detail_view_tabs(),
|
|
450
|
-
},
|
|
451
|
-
"list": {
|
|
452
|
-
"default_fields": list_display,
|
|
453
|
-
"all_fields": fields,
|
|
454
|
-
},
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
def _determine_detail_view_tabs(self):
|
|
458
|
-
"""Determine the layout for the detail view tabs that are intrinsic to this serializer."""
|
|
459
|
-
tabs = self.get_additional_detail_view_tabs()
|
|
460
|
-
|
|
461
|
-
if hasattr(self.Meta, "detail_view_config"):
|
|
462
|
-
detail_view_config = self._validate_view_config(self.Meta.detail_view_config)
|
|
463
|
-
else:
|
|
464
|
-
detail_view_config = self._get_default_detail_view_config()
|
|
465
|
-
detail_view_config = self._refine_detail_view_config(detail_view_config, tabs)
|
|
466
|
-
|
|
467
|
-
return {
|
|
468
|
-
bettertitle(self.Meta.model._meta.verbose_name): detail_view_config,
|
|
469
|
-
**tabs,
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
def get_additional_detail_view_tabs(self):
|
|
473
|
-
"""
|
|
474
|
-
Retrieve definitions of non-default detail view tabs.
|
|
475
|
-
|
|
476
|
-
By default provides an "Advanced" tab containing `self.advanced_tab_fields`, but subclasses
|
|
477
|
-
can override this to move additional serializer fields to this or other tabs.
|
|
478
|
-
|
|
479
|
-
Returns:
|
|
480
|
-
(dict): `{<tab label>: [{<panel label>: {"fields": [<list of fields>]}, ...}, ...], ...}`
|
|
481
|
-
"""
|
|
482
|
-
return {
|
|
483
|
-
"Advanced": [{"Object Details": {"fields": self.advanced_tab_fields}}],
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
def _get_default_detail_view_config(self):
|
|
487
|
-
"""
|
|
488
|
-
Generate detail view config for the view based on the serializer's fields.
|
|
489
|
-
|
|
490
|
-
Examples:
|
|
491
|
-
>>> DeviceSerializer._get_default_detail_view_config().
|
|
492
|
-
{
|
|
493
|
-
"layout":[
|
|
494
|
-
{
|
|
495
|
-
Device: {
|
|
496
|
-
"fields": ["name", "subdevice_role", "height", "comments"...]
|
|
497
|
-
}
|
|
498
|
-
},
|
|
499
|
-
{
|
|
500
|
-
Tags: {
|
|
501
|
-
"fields": ["tags"]
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
]
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
Returns:
|
|
508
|
-
(list): A list representing the view config.
|
|
509
|
-
"""
|
|
510
|
-
m2m_fields, other_fields = self._get_m2m_and_non_m2m_fields()
|
|
511
|
-
# TODO(timizuo): How do we get verbose_name of not model serializers?
|
|
512
|
-
model_verbose_name = self.Meta.model._meta.verbose_name
|
|
513
|
-
return {
|
|
514
|
-
"layout": [
|
|
515
|
-
{
|
|
516
|
-
bettertitle(model_verbose_name): {
|
|
517
|
-
"fields": [field["name"] for field in other_fields],
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
{field["label"]: {"fields": [field["name"]]} for field in m2m_fields},
|
|
521
|
-
]
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
def _get_m2m_and_non_m2m_fields(self):
|
|
525
|
-
"""
|
|
526
|
-
Retrieve the many-to-many (m2m) fields and other non-m2m fields from the serializer.
|
|
527
|
-
|
|
528
|
-
Returns:
|
|
529
|
-
A tuple containing two lists: m2m_fields and non m2m fields.
|
|
530
|
-
- m2m_fields: A list of dictionaries, each containing the name and label of an m2m field.
|
|
531
|
-
- non_m2m_fields: A list of dictionaries, each containing the name and label of a non m2m field.
|
|
532
|
-
"""
|
|
533
|
-
m2m_fields = []
|
|
534
|
-
non_m2m_fields = []
|
|
535
|
-
|
|
536
|
-
for field_name, field in self.fields.items():
|
|
537
|
-
if isinstance(field, drf_relations.ManyRelatedField):
|
|
538
|
-
m2m_fields.append({"name": field_name, "label": field.label or field_name})
|
|
539
|
-
else:
|
|
540
|
-
non_m2m_fields.append({"name": field_name, "label": field.label or field_name})
|
|
541
|
-
|
|
542
|
-
return m2m_fields, non_m2m_fields
|
|
543
|
-
|
|
544
|
-
def _refine_detail_view_config(self, detail_view_config, other_tabs):
|
|
545
|
-
"""
|
|
546
|
-
Refine the detail view config for the default tab (auto-generated, or as defined by Meta.detail_view_config).
|
|
547
|
-
|
|
548
|
-
- Remove fields that should never be present in the detail view config (e.g. `notes_url`).
|
|
549
|
-
- Ensure that fields that are already present in `other_tabs` aren't included in the detail view config.
|
|
550
|
-
- Ensure that certain fields such as `tags` are always included in the detail view config if applicable.
|
|
551
|
-
|
|
552
|
-
Args:
|
|
553
|
-
detail_view_config (dict): `{"layout": [{<left-column>}, {<right-column}], "include_others": False}`
|
|
554
|
-
|
|
555
|
-
Returns:
|
|
556
|
-
(list): `[{"Panel 1 Name": {"fields": ["field1", "field2", ...]}, "Panel 2 Name": ...}, {...}]`
|
|
557
|
-
"""
|
|
558
|
-
fields_to_always_move_to_right_column = [
|
|
559
|
-
"comments",
|
|
560
|
-
"tags",
|
|
561
|
-
]
|
|
562
|
-
fields_to_always_remove = [
|
|
563
|
-
# always handled explicitly by the UI
|
|
564
|
-
"display",
|
|
565
|
-
"status",
|
|
566
|
-
# not yet supported in the UI
|
|
567
|
-
"custom_fields",
|
|
568
|
-
"relationships",
|
|
569
|
-
"computed_fields",
|
|
570
|
-
# irrelevant to the UI
|
|
571
|
-
"notes_url",
|
|
572
|
-
]
|
|
573
|
-
fields_to_remove = fields_to_always_remove + fields_to_always_move_to_right_column
|
|
574
|
-
# Any field that's already present in another tab
|
|
575
|
-
for tab_layout in other_tabs.values():
|
|
576
|
-
for column in tab_layout:
|
|
577
|
-
for grouping in column.values():
|
|
578
|
-
fields_to_remove += grouping["fields"]
|
|
579
|
-
|
|
580
|
-
# Make a deepcopy to avoid altering view_config
|
|
581
|
-
view_config_layout = deepcopy(detail_view_config.get("layout"))
|
|
582
|
-
|
|
583
|
-
# Remove fields_to_remove from the view_config_layout
|
|
584
|
-
for column in view_config_layout:
|
|
585
|
-
for section in column.values():
|
|
586
|
-
for field in fields_to_remove:
|
|
587
|
-
if field in section["fields"]:
|
|
588
|
-
section["fields"].remove(field)
|
|
589
|
-
|
|
590
|
-
serializer_fields = list(self.fields)
|
|
591
|
-
|
|
592
|
-
# Add special-cased fields to right column
|
|
593
|
-
for field in fields_to_always_move_to_right_column:
|
|
594
|
-
if field in serializer_fields:
|
|
595
|
-
if len(view_config_layout) < 2:
|
|
596
|
-
view_config_layout.append({})
|
|
597
|
-
view_config_layout[1].setdefault(bettertitle(field), {}).setdefault("fields", []).append(field)
|
|
598
|
-
|
|
599
|
-
# Add fields not otherwise included in another tab, only if include_others is set to True.
|
|
600
|
-
if detail_view_config.get("include_others", False):
|
|
601
|
-
view_config_fields = [
|
|
602
|
-
field for column in view_config_layout for section in column.values() for field in section["fields"]
|
|
603
|
-
]
|
|
604
|
-
missing_fields = sorted(set(serializer_fields) - set(view_config_fields) - set(fields_to_remove))
|
|
605
|
-
view_config_layout[0]["Other Fields"] = {"fields": missing_fields}
|
|
606
|
-
|
|
607
|
-
return view_config_layout
|
|
608
|
-
|
|
609
|
-
def _validate_view_config(self, view_config):
|
|
610
|
-
"""Validate view config."""
|
|
611
|
-
# 1. Validate key `layout` is in view_config; as this is a required key is creating a view config
|
|
612
|
-
if not view_config["layout"]:
|
|
613
|
-
raise ViewConfigException("`layout` is a required key in creating a custom view_config")
|
|
614
|
-
|
|
615
|
-
# 2. Validate `Other Fields` is not part of a layout group name, as this is a reserved keyword for group names
|
|
616
|
-
for col in view_config["layout"]:
|
|
617
|
-
for group_name in col.keys():
|
|
618
|
-
if group_name in constants.RESERVED_NAMES_FOR_OBJECT_DETAIL_VIEW_SCHEMA:
|
|
619
|
-
raise ViewConfigException(f"`{group_name}` is a reserved group name keyword.")
|
|
620
|
-
|
|
621
|
-
return view_config
|
|
622
|
-
|
|
623
389
|
def build_field(self, field_name, info, model_class, nested_depth):
|
|
624
390
|
"""
|
|
625
391
|
Return a two tuple of (cls, kwargs) to build a serializer field with.
|
|
@@ -759,27 +525,27 @@ class ValidatedModelSerializer(BaseModelSerializer):
|
|
|
759
525
|
validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
|
|
760
526
|
"""
|
|
761
527
|
|
|
762
|
-
def validate(self,
|
|
528
|
+
def validate(self, data):
|
|
763
529
|
# Remove custom fields data and tags (if any) prior to model validation
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
530
|
+
attrs = data.copy()
|
|
531
|
+
attrs.pop("custom_fields", None)
|
|
532
|
+
attrs.pop("relationships", None)
|
|
533
|
+
attrs.pop("tags", None)
|
|
768
534
|
|
|
769
535
|
# Skip ManyToManyFields
|
|
770
536
|
for field in self.Meta.model._meta.get_fields():
|
|
771
537
|
if isinstance(field, models.ManyToManyField):
|
|
772
|
-
|
|
538
|
+
attrs.pop(field.name, None)
|
|
773
539
|
|
|
774
540
|
# Run clean() on an instance of the model
|
|
775
541
|
if self.instance is None:
|
|
776
|
-
instance = self.Meta.model(**
|
|
542
|
+
instance = self.Meta.model(**attrs)
|
|
777
543
|
else:
|
|
778
544
|
instance = self.instance
|
|
779
|
-
for k, v in
|
|
545
|
+
for k, v in attrs.items():
|
|
780
546
|
setattr(instance, k, v)
|
|
781
547
|
instance.full_clean()
|
|
782
|
-
return
|
|
548
|
+
return data
|
|
783
549
|
|
|
784
550
|
|
|
785
551
|
class WritableNestedSerializer(BaseModelSerializer):
|
|
@@ -1106,3 +872,17 @@ class NautobotModelSerializer(
|
|
|
1106
872
|
|
|
1107
873
|
Can also be used for models derived from BaseModel, so long as they support custom fields, notes, and relationships.
|
|
1108
874
|
"""
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
#
|
|
878
|
+
# Tools
|
|
879
|
+
#
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class RenderJinjaSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
|
883
|
+
"""Serializer for RenderJinjaView."""
|
|
884
|
+
|
|
885
|
+
template_code = serializers.CharField(required=True)
|
|
886
|
+
context = serializers.DictField(default=dict)
|
|
887
|
+
rendered_template = serializers.CharField(read_only=True)
|
|
888
|
+
rendered_template_lines = serializers.ListField(read_only=True, child=serializers.CharField())
|