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
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<a href="{% static 'docs/index.html' %}">Docs</a>
|
|
42
42
|
{% endif %}
|
|
43
43
|
·
|
|
44
|
+
<img src="{% static 'img/jinja_logo.svg' %}" style="height:20px"> <a href="{% url 'render_jinja_template' %}">Jinja Renderer</a> ·
|
|
44
45
|
<i class="mdi mdi-cloud-braces text-primary"></i> <a href="{% url 'api_docs' %}">API</a> ·
|
|
45
46
|
<i class="mdi mdi-graphql text-primary"></i> <a href="{% url 'graphql' %}">GraphQL</a> ·
|
|
46
47
|
<i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}">Code</a> ·
|
|
@@ -47,6 +47,3 @@
|
|
|
47
47
|
<meta name="theme-color" content="#ffffff">
|
|
48
48
|
<meta charset="UTF-8">
|
|
49
49
|
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
|
50
|
-
{% if settings.PUBLISH_ROBOTS_TXT %}
|
|
51
|
-
<meta name="robots" content="noindex, nofollow">
|
|
52
|
-
{% endif %}
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
{% endcomment %}
|
|
57
57
|
{% url button_details.link as button_url %}
|
|
58
58
|
{% if button_url %}
|
|
59
|
-
<a href="{{ button_url }}"
|
|
59
|
+
<a href="{{ button_url }}{{ button_details.querystring }}"
|
|
60
60
|
data-button-weight="{{ button_details.weight }}"
|
|
61
61
|
class="btn btn-xs btn-{{ button_details.button_class }}"
|
|
62
62
|
title="{{ button_title }}">
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<strong>Relationships</strong>
|
|
8
8
|
</div>
|
|
9
9
|
<table class="table table-hover panel-body attr-table">
|
|
10
|
-
{% include 'inc/relationships_table_rows.html' with relationships_data=
|
|
10
|
+
{% include 'inc/relationships_table_rows.html' with relationships_data=relationships %}
|
|
11
11
|
</table>
|
|
12
12
|
</div>
|
|
13
13
|
{% endif %}
|
|
@@ -364,6 +364,9 @@ SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "{{ secret_key }}")
|
|
|
364
364
|
# if "NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY" in os.environ and os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"] != "":
|
|
365
365
|
# DEVICE_NAME_AS_NATURAL_KEY = is_truthy(os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"])
|
|
366
366
|
|
|
367
|
+
# Event Brokers
|
|
368
|
+
# EVENT_BROKERS = {}
|
|
369
|
+
|
|
367
370
|
# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and
|
|
368
371
|
# by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
|
|
369
372
|
# Defaults to [].
|
|
@@ -490,9 +493,6 @@ INSTALLATION_METRICS_ENABLED = is_truthy(os.getenv("NAUTOBOT_INSTALLATION_METRIC
|
|
|
490
493
|
# if "NAUTOBOT_PREFER_IPV4" in os.environ and os.environ["NAUTOBOT_PREFER_IPV4"] != "":
|
|
491
494
|
# PREFER_IPV4 = is_truthy(os.environ["NAUTOBOT_PREFER_IPV4"])
|
|
492
495
|
|
|
493
|
-
# Publish a simple "no-index" robots.txt for Nautobot?
|
|
494
|
-
# PUBLISH_ROBOTS_TXT = is_truthy(os.getenv("NAUTOBOT_PUBLISH_ROBOTS_TXT", "True"))
|
|
495
|
-
|
|
496
496
|
# Default height and width in pixels of a single rack unit in rendered rack elevations. Defaults are 22 and 230
|
|
497
497
|
#
|
|
498
498
|
# if (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% load render_table from django_tables2 %}
|
|
2
|
+
{% load helpers %}
|
|
2
3
|
|
|
3
4
|
<div class="panel panel-{{ panel_class|default:'default' }}">
|
|
4
5
|
{% if heading %}
|
|
@@ -13,4 +14,15 @@
|
|
|
13
14
|
{% else %}
|
|
14
15
|
<div class="panel-body text-muted">None</div>
|
|
15
16
|
{% endif %}
|
|
17
|
+
{% if add_obj_url %}
|
|
18
|
+
<div class="panel-footer noprint">
|
|
19
|
+
<div class="pull-right">
|
|
20
|
+
<a href="{{ add_obj_url }}" class="btn btn-primary btn-xs">
|
|
21
|
+
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add {{ table.data.data.model|meta:"verbose_name" }}
|
|
22
|
+
</a>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="clearfix"></div>
|
|
26
|
+
</div>
|
|
27
|
+
{% endif %}
|
|
16
28
|
</div>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% load form_helpers %}
|
|
3
|
+
{% load helpers %}
|
|
4
|
+
{% load static %}
|
|
5
|
+
|
|
6
|
+
{% block extra_styles %}
|
|
7
|
+
<style type="text/css">
|
|
8
|
+
.button-container {
|
|
9
|
+
margin-bottom: 24px;
|
|
10
|
+
}
|
|
11
|
+
#render_jinja_template_form textarea {
|
|
12
|
+
resize: vertical;
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
</style>
|
|
17
|
+
{% endblock extra_styles %}
|
|
18
|
+
|
|
19
|
+
{% block header %}
|
|
20
|
+
<h1>{% block title %}Jinja Template Renderer{% endblock %}</h1>
|
|
21
|
+
<hr>
|
|
22
|
+
{% endblock %}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
{% block content %}
|
|
27
|
+
<form class="form form-horizontal" onsubmit="handleFormSubmit(event)" id="render_jinja_template_form">
|
|
28
|
+
<div class="row">
|
|
29
|
+
<div class="col-lg-6 col-md-6">
|
|
30
|
+
<div class="panel panel-default">
|
|
31
|
+
<div class="panel-heading"><strong>Template</strong></div>
|
|
32
|
+
<div class="panel-body">
|
|
33
|
+
<div class="form-group">
|
|
34
|
+
<label class="col-md-3 control-label required" for="id_template_code">Jinja Template</label>
|
|
35
|
+
<div class="col-md-9">
|
|
36
|
+
<textarea cols="40" rows="10" class="form-control" id="id_template_code" required=""></textarea>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="form-group">
|
|
40
|
+
<label class="col-md-3 control-label" for="id_context">Data</label>
|
|
41
|
+
<div class="col-md-9">
|
|
42
|
+
<textarea cols="40" rows="10" class="form-control" placeholder="{}" id="id_context">{}</textarea>
|
|
43
|
+
<span class="help-block">Enter data in <a href="https://json.org/">JSON</a> format.</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="button-container text-right">
|
|
49
|
+
<button type="submit" class="btn btn-primary">Render</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="col-lg-6 col-md-6">
|
|
53
|
+
<div class="panel panel-default">
|
|
54
|
+
<div class="panel-heading">
|
|
55
|
+
<strong>Rendered Template</strong>
|
|
56
|
+
<button type="button" class="btn btn-inline btn-default copy-rendered-template" data-clipboard-target="#rendered_template">
|
|
57
|
+
<span class="mdi mdi-content-copy"></span>
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="panel-body">
|
|
61
|
+
<textarea readonly="readonly" cols="40" rows="23" class="form-control" placeholder="Rendered Template" id="rendered_template"></textarea>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</form>
|
|
67
|
+
{% endblock content %}
|
|
68
|
+
|
|
69
|
+
{% block javascript %}
|
|
70
|
+
{{ block.super }}
|
|
71
|
+
<script>
|
|
72
|
+
new ClipboardJS('.copy-rendered-template');
|
|
73
|
+
const sanitize = function(string) {
|
|
74
|
+
return string.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
75
|
+
};
|
|
76
|
+
async function handleFormSubmit(event) {
|
|
77
|
+
event.preventDefault(); // Prevent default form submission
|
|
78
|
+
|
|
79
|
+
// Check that the context is valid JSON
|
|
80
|
+
const context = document.getElementById("id_context");
|
|
81
|
+
var parsed_context;
|
|
82
|
+
try {
|
|
83
|
+
parsed_context = JSON.parse(context.value);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
context.setCustomValidity("Invalid JSON");
|
|
86
|
+
context.reportValidity();
|
|
87
|
+
context.setCustomValidity("");
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const rendered_template = document.getElementById("rendered_template");
|
|
93
|
+
rendered_template.innerHTML = "Loading...";
|
|
94
|
+
const template_code = document.getElementById("id_template_code").value;
|
|
95
|
+
const url = "{% url 'core-api:render_jinja_template' %}";
|
|
96
|
+
const data = {
|
|
97
|
+
template_code: template_code,
|
|
98
|
+
context: parsed_context
|
|
99
|
+
}
|
|
100
|
+
const response = await fetch(url, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: JSON.stringify(data),
|
|
103
|
+
headers: {"Content-Type": "application/json", "X-CSRFTOKEN": "{{ csrf_token }}"}
|
|
104
|
+
});
|
|
105
|
+
const responseData = await response.json();
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const msg = responseData.detail ? responseData.detail : response.statusText;
|
|
108
|
+
rendered_template.innerHTML = sanitize(`An error occurred:\n\n${msg}`);
|
|
109
|
+
} else {
|
|
110
|
+
rendered_template.innerHTML = sanitize(responseData.rendered_template);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
rendered_template.innerHTML = sanitize(`An error occurred:\n\n${error.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
{% endblock javascript %}
|
|
@@ -21,6 +21,7 @@ import yaml
|
|
|
21
21
|
|
|
22
22
|
from nautobot.apps.config import get_app_settings_or_config
|
|
23
23
|
from nautobot.core import forms
|
|
24
|
+
from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
|
|
24
25
|
from nautobot.core.utils import color, config, data, logging as nautobot_logging, lookup
|
|
25
26
|
from nautobot.core.utils.requests import add_nautobot_version_query_param_to_url
|
|
26
27
|
|
|
@@ -118,6 +119,26 @@ def placeholder(value):
|
|
|
118
119
|
return HTML_NONE
|
|
119
120
|
|
|
120
121
|
|
|
122
|
+
@library.filter()
|
|
123
|
+
@register.filter()
|
|
124
|
+
def pre_tag(value):
|
|
125
|
+
"""Render a value within `<pre></pre>` tags to enable formatting.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
value (any): Input value, can be any variable.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
(str): Value wrapped in `<pre></pre>` tags.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> pre_tag("")
|
|
135
|
+
'<pre></pre>'
|
|
136
|
+
>>> pre_tag("hello")
|
|
137
|
+
'<pre>hello</pre>'
|
|
138
|
+
"""
|
|
139
|
+
return format_html("<pre>{}</pre>", value)
|
|
140
|
+
|
|
141
|
+
|
|
121
142
|
@library.filter()
|
|
122
143
|
@register.filter()
|
|
123
144
|
def add_html_id(element_str, id_str):
|
|
@@ -202,6 +223,29 @@ def render_json(value, syntax_highlight=True, pretty_print=False):
|
|
|
202
223
|
|
|
203
224
|
Unless `syntax_highlight=False` is specified, the returned string will be wrapped in a
|
|
204
225
|
`<code class="language-json>` HTML tag to flag it for syntax highlighting by highlight.js.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
value (any): Input value, can be any variable.
|
|
229
|
+
syntax_highlight (bool): Whether to highlight the JSON syntax or not.
|
|
230
|
+
pretty_print (bool): Wraps rendered and highlighted JSON in <pre> tag for better code display.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
(str): HTML
|
|
234
|
+
'<code class="language-json">{"json_key": "json_value"}</code>' if only syntax_highlight is True
|
|
235
|
+
- or -
|
|
236
|
+
'<pre><code class="language-json">{"json_key": "json_value"}</code></pre>' if both syntax_highlight and pretty_print are True
|
|
237
|
+
- or -
|
|
238
|
+
'{"json_key": "json_value"}' if only pretty_print is True (both syntax_highlight and pretty_print must be True for pretty print)
|
|
239
|
+
|
|
240
|
+
Examples:
|
|
241
|
+
>>> render_json({"key": "value"})
|
|
242
|
+
'<code class="language-json">{"key": "value"}</code>'
|
|
243
|
+
>>> render_json({"key": "value"}, syntax_highlight=False)
|
|
244
|
+
'{"key": "value"}'
|
|
245
|
+
>>> render_json({"key": "value"}, pretty_print=True)
|
|
246
|
+
'<pre><code class="language-json">{"key": "value"}</code></pre>'
|
|
247
|
+
>>> render_json({"key": "value"}, syntax_highlight=False, pretty_print=True)
|
|
248
|
+
'{"key": "value"}'
|
|
205
249
|
"""
|
|
206
250
|
rendered_json = json.dumps(value, indent=4, sort_keys=True, ensure_ascii=False)
|
|
207
251
|
if syntax_highlight:
|
|
@@ -616,6 +660,57 @@ def render_uptime(seconds):
|
|
|
616
660
|
)
|
|
617
661
|
|
|
618
662
|
|
|
663
|
+
@library.filter()
|
|
664
|
+
@register.filter()
|
|
665
|
+
def dbm(value):
|
|
666
|
+
"""Display value as dBm."""
|
|
667
|
+
return f"{value} dBm" if value else placeholder(value)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
@library.filter()
|
|
671
|
+
@register.filter()
|
|
672
|
+
def hyperlinked_field(value, hyperlink=None):
|
|
673
|
+
"""Render a value as a hyperlink."""
|
|
674
|
+
if not value:
|
|
675
|
+
return placeholder(value)
|
|
676
|
+
hyperlink = hyperlink or value
|
|
677
|
+
return format_html('<a href="{}">{}</a>', hyperlink, value)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
@library.filter()
|
|
681
|
+
@register.filter()
|
|
682
|
+
def render_content_types(value):
|
|
683
|
+
"""Render sorted by model and app_label ContentTypes value"""
|
|
684
|
+
if not value.exists():
|
|
685
|
+
return HTML_NONE
|
|
686
|
+
output = format_html("<ul>")
|
|
687
|
+
sorted_value = value.order_by("app_label", "model")
|
|
688
|
+
for content_type in sorted_value:
|
|
689
|
+
output += format_html("<li>{content_type}</li>", content_type=content_type)
|
|
690
|
+
output += format_html("</ul>")
|
|
691
|
+
|
|
692
|
+
return output
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
@library.filter()
|
|
696
|
+
@register.filter()
|
|
697
|
+
def render_ancestor_hierarchy(value):
|
|
698
|
+
"""Renders a nested HTML list representing the hierarchy of ancestors for a given object."""
|
|
699
|
+
result = format_html('<ul class="tree-hierarchy">')
|
|
700
|
+
append_to_result = format_html("</ul>")
|
|
701
|
+
for ancestor in value.ancestors():
|
|
702
|
+
nestable_tag = format_html('<span title="nestable">↺</span>' if getattr(ancestor, "nestable", False) else "")
|
|
703
|
+
result += format_html(
|
|
704
|
+
"<li>{value} {nestable_tag}<ul>", value=hyperlinked_object(ancestor, "name"), nestable_tag=nestable_tag
|
|
705
|
+
)
|
|
706
|
+
append_to_result += format_html("</ul></li>")
|
|
707
|
+
nestable_tag = format_html('<span title="nestable">↺</span>' if getattr(value, "nestable", False) else "")
|
|
708
|
+
result += format_html("<li><strong>{value} {nestable_tag}</strong></li>", value=value, nestable_tag=nestable_tag)
|
|
709
|
+
result += append_to_result
|
|
710
|
+
|
|
711
|
+
return result
|
|
712
|
+
|
|
713
|
+
|
|
619
714
|
#
|
|
620
715
|
# Tags
|
|
621
716
|
#
|
|
@@ -645,15 +740,15 @@ def querystring(request, **kwargs):
|
|
|
645
740
|
|
|
646
741
|
|
|
647
742
|
@register.simple_tag()
|
|
648
|
-
def table_config_button(table, table_name=None, extra_classes=""):
|
|
743
|
+
def table_config_button(table, table_name=None, extra_classes="", disabled=False):
|
|
649
744
|
if table_name is None:
|
|
650
745
|
table_name = table.__class__.__name__
|
|
651
746
|
html_template = (
|
|
652
747
|
'<button type="button" class="btn btn-default {}'
|
|
653
|
-
'" data-toggle="modal" data-target="#{}_config" title="Configure table">'
|
|
748
|
+
'" data-toggle="modal" data-target="#{}_config" {} title="Configure table">'
|
|
654
749
|
'<i class="mdi mdi-cog"></i> Configure</button>'
|
|
655
750
|
)
|
|
656
|
-
return format_html(html_template, extra_classes, table_name)
|
|
751
|
+
return format_html(html_template, extra_classes, table_name, 'disabled="disabled"' if disabled else "")
|
|
657
752
|
|
|
658
753
|
|
|
659
754
|
@register.simple_tag()
|
|
@@ -795,13 +890,7 @@ def saved_view_modal(
|
|
|
795
890
|
"clear_view",
|
|
796
891
|
]
|
|
797
892
|
|
|
798
|
-
|
|
799
|
-
table_name = None
|
|
800
|
-
if hasattr(view_class, "table"):
|
|
801
|
-
table_name = view_class.table.__name__
|
|
802
|
-
if hasattr(view_class, "table_class"):
|
|
803
|
-
table_name = view_class.table_class.__name__
|
|
804
|
-
|
|
893
|
+
table_name = lookup.get_table_for_model(model).__name__
|
|
805
894
|
for param in non_filter_params:
|
|
806
895
|
if param == "saved_view":
|
|
807
896
|
current_saved_view_pk = filters_applied.pop(param, None)
|
|
@@ -852,11 +941,11 @@ def saved_view_modal(
|
|
|
852
941
|
elif current_saved_view is not None and not per_page:
|
|
853
942
|
# no changes made, display current saved view pagination count
|
|
854
943
|
param_dict["per_page"] = current_saved_view.config.get(
|
|
855
|
-
"pagination_count", config.get_settings_or_config("PAGINATE_COUNT")
|
|
944
|
+
"pagination_count", config.get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
|
|
856
945
|
)
|
|
857
946
|
else:
|
|
858
947
|
# display default pagination count
|
|
859
|
-
param_dict["per_page"] = config.get_settings_or_config("PAGINATE_COUNT")
|
|
948
|
+
param_dict["per_page"] = config.get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
|
|
860
949
|
|
|
861
950
|
if sort_order:
|
|
862
951
|
# user made changes to saved view sort order
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from django import template
|
|
2
|
+
from django.utils.html import format_html_join
|
|
3
|
+
|
|
4
|
+
from nautobot.core.utils.lookup import get_view_for_model
|
|
5
|
+
from nautobot.core.views.utils import get_obj_from_context
|
|
6
|
+
|
|
7
|
+
register = template.Library()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@register.simple_tag(takes_context=True)
|
|
11
|
+
def render_tabs_labels(context, tabs):
|
|
12
|
+
"""Render the tab labels for each Tab in the given `tabs` with the given `context`."""
|
|
13
|
+
if tabs is not None:
|
|
14
|
+
return format_html_join("\n", "{}", ([tab.render_label_wrapper(context)] for tab in tabs))
|
|
15
|
+
return ""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@register.simple_tag(takes_context=True)
|
|
19
|
+
def render_components(context, components):
|
|
20
|
+
"""Render each component in the given `components` with the given `context`."""
|
|
21
|
+
if components is not None:
|
|
22
|
+
return format_html_join("\n", "{}", ([component.render(context)] for component in components))
|
|
23
|
+
return ""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@register.simple_tag(takes_context=True)
|
|
27
|
+
def render_detail_view_extra_buttons(context):
|
|
28
|
+
"""
|
|
29
|
+
Render the "extra_buttons" if any from the base detail view associated with the context object.
|
|
30
|
+
|
|
31
|
+
This makes it possible for "extra" tabs (such as Changelog and Notes, and any added by App TemplateExtensions)
|
|
32
|
+
to automatically still render any `extra_buttons` defined by the base detail view, without the tab-specific views
|
|
33
|
+
needing to explicitly inherit from the base view.
|
|
34
|
+
"""
|
|
35
|
+
obj = get_obj_from_context(context)
|
|
36
|
+
base_detail_view = get_view_for_model(obj)
|
|
37
|
+
object_detail_content = getattr(base_detail_view, "object_detail_content", None)
|
|
38
|
+
if object_detail_content is not None and object_detail_content.extra_buttons:
|
|
39
|
+
return render_components(context, object_detail_content.extra_buttons)
|
|
40
|
+
return ""
|
nautobot/core/testing/api.py
CHANGED
|
@@ -16,7 +16,6 @@ from rest_framework.test import APITransactionTestCase as _APITransactionTestCas
|
|
|
16
16
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
17
17
|
from nautobot.core.models import fields as core_fields
|
|
18
18
|
from nautobot.core.models.tree_queries import TreeModel
|
|
19
|
-
from nautobot.core.templatetags.helpers import bettertitle
|
|
20
19
|
from nautobot.core.testing import mixins, utils, views
|
|
21
20
|
from nautobot.core.utils import lookup
|
|
22
21
|
from nautobot.core.utils.data import is_uuid
|
|
@@ -169,6 +168,10 @@ class APIViewTestCases:
|
|
|
169
168
|
if issubclass(self.model, extras_models.ChangeLoggedModel):
|
|
170
169
|
self.assertIn("created", response.data)
|
|
171
170
|
self.assertIn("last_updated", response.data)
|
|
171
|
+
if hasattr(self.model, "notes") and isinstance(instance1.notes, QuerySet):
|
|
172
|
+
self.assertIn("notes_url", response.data)
|
|
173
|
+
self.assertIn(f"{url}notes/", str(response.data["notes_url"]))
|
|
174
|
+
self.assertIn(instance1.get_notes_url(api=True), str(response.data["notes_url"]))
|
|
172
175
|
# Fields that should be absent by default (opt-in fields):
|
|
173
176
|
self.assertNotIn("computed_fields", response.data)
|
|
174
177
|
self.assertNotIn("relationships", response.data)
|
|
@@ -218,58 +221,6 @@ class APIViewTestCases:
|
|
|
218
221
|
response = self.client.options(url, **self.header)
|
|
219
222
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
220
223
|
|
|
221
|
-
with self.subTest("Assert Detail View Config is generated well"):
|
|
222
|
-
# Namings Help
|
|
223
|
-
# 1. detail_view_config: This is the detail view config set in the serializer.Meta.detail_view_config
|
|
224
|
-
# 2. detail_view_schema: This is the retrieve schema generated from an OPTIONS request.
|
|
225
|
-
# 3. advanced_view_schema: This is the advanced tab schema generated from an OPTIONS request.
|
|
226
|
-
serializer = get_serializer_for_model(self._get_queryset().model)
|
|
227
|
-
advanced_view_schema = response.data["view_options"]["retrieve"]["tabs"]["Advanced"]
|
|
228
|
-
|
|
229
|
-
# Get default advanced tab fields
|
|
230
|
-
self.assertEqual(len(advanced_view_schema), 1)
|
|
231
|
-
self.assertIn("Object Details", advanced_view_schema[0])
|
|
232
|
-
advanced_tab_fields = advanced_view_schema[0].get("Object Details")["fields"]
|
|
233
|
-
|
|
234
|
-
if detail_view_config := getattr(serializer.Meta, "detail_view_config", None):
|
|
235
|
-
detail_view_schema = response.data["view_options"]["retrieve"]["tabs"][
|
|
236
|
-
bettertitle(self._get_queryset().model._meta.verbose_name)
|
|
237
|
-
]
|
|
238
|
-
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
239
|
-
|
|
240
|
-
# According to convention, fields in the advanced tab fields should not exist in
|
|
241
|
-
# the `detail_view_schema`. Assert this is True.
|
|
242
|
-
with self.subTest("Assert advanced tab fields should not exist in the detail_view_schema."):
|
|
243
|
-
if detail_view_config.get("include_others"):
|
|
244
|
-
# Handle "Other Fields" section specially as "Other Field" is dynamically added
|
|
245
|
-
# by Nautobot and is not part of the serializer-defined detail_view_config
|
|
246
|
-
other_fields = detail_view_schema[0]["Other Fields"]["fields"]
|
|
247
|
-
for field in advanced_tab_fields:
|
|
248
|
-
self.assertNotIn(field, other_fields)
|
|
249
|
-
|
|
250
|
-
for col_idx, col in enumerate(detail_view_schema):
|
|
251
|
-
for group_title, group in col.items():
|
|
252
|
-
if group_title == "Other Fields":
|
|
253
|
-
continue
|
|
254
|
-
group_fields = group["fields"]
|
|
255
|
-
# Config on the serializer
|
|
256
|
-
if (
|
|
257
|
-
col_idx < len(detail_view_config["layout"])
|
|
258
|
-
and group_title in detail_view_config["layout"][col_idx]
|
|
259
|
-
):
|
|
260
|
-
fields = detail_view_config["layout"][col_idx][group_title]["fields"]
|
|
261
|
-
else:
|
|
262
|
-
fields = []
|
|
263
|
-
|
|
264
|
-
# Fields that are in the detail_view_schema must not be in the advanced tab as well
|
|
265
|
-
for field in group_fields:
|
|
266
|
-
self.assertNotIn(field, advanced_tab_fields)
|
|
267
|
-
|
|
268
|
-
# Fields that are explicit in the detail_view_config must remain as such in the schema
|
|
269
|
-
for field in fields:
|
|
270
|
-
if field not in advanced_tab_fields:
|
|
271
|
-
self.assertIn(field, group_fields)
|
|
272
|
-
|
|
273
224
|
class ListObjectsViewTestCase(APITestCase):
|
|
274
225
|
choices_fields = None
|
|
275
226
|
filterset = None
|
|
@@ -637,10 +588,7 @@ class APIViewTestCases:
|
|
|
637
588
|
POST a single object with permission.
|
|
638
589
|
"""
|
|
639
590
|
# Add object-level permission
|
|
640
|
-
|
|
641
|
-
obj_perm.save()
|
|
642
|
-
obj_perm.users.add(self.user)
|
|
643
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
591
|
+
self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
|
|
644
592
|
|
|
645
593
|
initial_count = self._get_queryset().count()
|
|
646
594
|
for i, create_data in enumerate(self.create_data):
|
|
@@ -683,10 +631,10 @@ class APIViewTestCases:
|
|
|
683
631
|
self.fail("Couldn't find a single deletable object!")
|
|
684
632
|
|
|
685
633
|
# Add object-level permission
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
634
|
+
self.add_permissions(
|
|
635
|
+
f"{self.model._meta.app_label}.add_{self.model._meta.model_name}",
|
|
636
|
+
f"{self.model._meta.app_label}.view_{self.model._meta.model_name}",
|
|
637
|
+
)
|
|
690
638
|
|
|
691
639
|
response = self.client.get(self._get_detail_url(instance) + "?format=csv", **self.header)
|
|
692
640
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
@@ -707,11 +655,7 @@ class APIViewTestCases:
|
|
|
707
655
|
self.assertHttpStatus(response, status.HTTP_201_CREATED, csv_data)
|
|
708
656
|
# Note that create via CSV is always treated as a bulk-create, and so the response is always a list of dicts
|
|
709
657
|
new_instance = self._get_queryset().get(pk=response.data[0]["id"])
|
|
710
|
-
|
|
711
|
-
self.assertNotEqual(new_instance.pk, orig_pk)
|
|
712
|
-
else:
|
|
713
|
-
# for our non-integer PKs, we're expecting the creation to respect the requested PK
|
|
714
|
-
self.assertEqual(new_instance.pk, orig_pk)
|
|
658
|
+
self.assertNotEqual(new_instance.pk, orig_pk)
|
|
715
659
|
|
|
716
660
|
new_serializer = serializer_class(new_instance, context={"request": None})
|
|
717
661
|
new_data = new_serializer.data
|
|
@@ -739,10 +683,7 @@ class APIViewTestCases:
|
|
|
739
683
|
POST a set of objects in a single request.
|
|
740
684
|
"""
|
|
741
685
|
# Add object-level permission
|
|
742
|
-
|
|
743
|
-
obj_perm.save()
|
|
744
|
-
obj_perm.users.add(self.user)
|
|
745
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
686
|
+
self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
|
|
746
687
|
|
|
747
688
|
initial_count = self._get_queryset().count()
|
|
748
689
|
response = self.client.post(self._get_list_url(), self.create_data, format="json", **self.header)
|
|
@@ -873,10 +814,10 @@ class APIViewTestCases:
|
|
|
873
814
|
"""GET and then PUT an object and verify that it's accepted and unchanged."""
|
|
874
815
|
self.maxDiff = None
|
|
875
816
|
# Add object-level permission
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
817
|
+
self.add_permissions(
|
|
818
|
+
f"{self.model._meta.app_label}.view_{self.model._meta.model_name}",
|
|
819
|
+
f"{self.model._meta.app_label}.change_{self.model._meta.model_name}",
|
|
820
|
+
)
|
|
880
821
|
|
|
881
822
|
instance = self._get_queryset().first()
|
|
882
823
|
url = self._get_detail_url(instance)
|
|
@@ -907,10 +848,7 @@ class APIViewTestCases:
|
|
|
907
848
|
self.skipTest("Bulk update data not set")
|
|
908
849
|
|
|
909
850
|
# Add object-level permission
|
|
910
|
-
|
|
911
|
-
obj_perm.save()
|
|
912
|
-
obj_perm.users.add(self.user)
|
|
913
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
851
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
914
852
|
|
|
915
853
|
id_list = list(self._get_queryset().values_list("id", flat=True)[:3])
|
|
916
854
|
self.assertEqual(len(id_list), 3, "Insufficient number of objects to test bulk update")
|
|
@@ -954,24 +892,11 @@ class APIViewTestCases:
|
|
|
954
892
|
self.assertIn("actions", data)
|
|
955
893
|
|
|
956
894
|
# Grab any field that has choices defined (fields with enums)
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
]
|
|
962
|
-
):
|
|
963
|
-
schema = data["schema"]
|
|
964
|
-
props = schema["properties"]
|
|
965
|
-
fields = props.keys()
|
|
966
|
-
field_choices = set()
|
|
967
|
-
for field_name in fields:
|
|
968
|
-
obj = props[field_name]
|
|
969
|
-
if "enum" in obj and "enumNames" in obj:
|
|
970
|
-
enum = obj["enum"]
|
|
971
|
-
# Zipping to assert that the enum and the mapping have the same number of items.
|
|
972
|
-
model_field_choices = dict(zip(obj["enumNames"], enum))
|
|
973
|
-
self.assertEqual(len(enum), len(model_field_choices))
|
|
974
|
-
field_choices.add(field_name)
|
|
895
|
+
field_choices = {}
|
|
896
|
+
if "POST" in data["actions"]:
|
|
897
|
+
field_choices = {k for k, v in data["actions"]["POST"].items() if "choices" in v}
|
|
898
|
+
elif "PUT" in data["actions"]:
|
|
899
|
+
field_choices = {k for k, v in data["actions"]["PUT"].items() if "choices" in v}
|
|
975
900
|
else:
|
|
976
901
|
self.fail(f"Neither PUT nor POST are available actions in: {data['actions']}")
|
|
977
902
|
|
|
@@ -1026,10 +951,7 @@ class APIViewTestCases:
|
|
|
1026
951
|
url = self._get_detail_url(instance)
|
|
1027
952
|
|
|
1028
953
|
# Add object-level permission
|
|
1029
|
-
|
|
1030
|
-
obj_perm.save()
|
|
1031
|
-
obj_perm.users.add(self.user)
|
|
1032
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
954
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
1033
955
|
|
|
1034
956
|
response = self.client.delete(url, **self.header)
|
|
1035
957
|
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
|
@@ -1046,10 +968,7 @@ class APIViewTestCases:
|
|
|
1046
968
|
"""
|
|
1047
969
|
id_list = self.get_deletable_object_pks()
|
|
1048
970
|
# Add object-level permission
|
|
1049
|
-
|
|
1050
|
-
obj_perm.save()
|
|
1051
|
-
obj_perm.users.add(self.user)
|
|
1052
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
971
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
1053
972
|
|
|
1054
973
|
data = [{"id": id} for id in id_list]
|
|
1055
974
|
|
|
@@ -1061,30 +980,6 @@ class APIViewTestCases:
|
|
|
1061
980
|
class NotesURLViewTestCase(APITestCase):
|
|
1062
981
|
"""Validate Notes URL on objects that have the Note model Mixin."""
|
|
1063
982
|
|
|
1064
|
-
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1065
|
-
def test_notes_url_on_object(self):
|
|
1066
|
-
if not hasattr(self.model, "notes"):
|
|
1067
|
-
self.skipTest("Model doesn't appear to support Notes")
|
|
1068
|
-
instance = self._get_queryset().first()
|
|
1069
|
-
if not isinstance(instance.notes, QuerySet):
|
|
1070
|
-
self.skipTest("Model has a notes field but it doesn't appear to be Notes")
|
|
1071
|
-
|
|
1072
|
-
# Add object-level permission
|
|
1073
|
-
obj_perm = users_models.ObjectPermission(
|
|
1074
|
-
name="Test permission",
|
|
1075
|
-
constraints={"pk": instance.pk},
|
|
1076
|
-
actions=["view"],
|
|
1077
|
-
)
|
|
1078
|
-
obj_perm.save()
|
|
1079
|
-
obj_perm.users.add(self.user)
|
|
1080
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1081
|
-
url = self._get_detail_url(instance)
|
|
1082
|
-
response = self.client.get(url, **self.header)
|
|
1083
|
-
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1084
|
-
self.assertIn("notes_url", response.data)
|
|
1085
|
-
self.assertIn(f"{url}notes/", str(response.data["notes_url"]))
|
|
1086
|
-
self.assertIn(instance.get_notes_url(api=True), str(response.data["notes_url"]))
|
|
1087
|
-
|
|
1088
983
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1089
984
|
def test_notes_url_functionality(self):
|
|
1090
985
|
if not hasattr(self.model, "notes"):
|