nautobot 2.3.16__py3-none-any.whl → 2.4.0__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/__init__.py +1 -1
- nautobot/apps/api.py +8 -10
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/config.py +32 -3
- nautobot/apps/events.py +19 -0
- nautobot/apps/exceptions.py +0 -2
- nautobot/apps/factory.py +2 -2
- nautobot/apps/filters.py +1 -1
- nautobot/apps/forms.py +20 -20
- nautobot/apps/graphql.py +2 -2
- nautobot/apps/jobs.py +8 -8
- nautobot/apps/models.py +19 -19
- nautobot/apps/tables.py +1 -1
- nautobot/apps/testing.py +10 -10
- nautobot/apps/ui.py +44 -9
- nautobot/apps/utils.py +7 -15
- nautobot/apps/views.py +8 -6
- nautobot/circuits/api/serializers.py +1 -0
- nautobot/circuits/api/views.py +4 -8
- nautobot/circuits/navigation.py +0 -57
- nautobot/circuits/templates/circuits/circuit_create.html +1 -7
- 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 +140 -23
- nautobot/cloud/api/views.py +6 -10
- nautobot/cloud/factory.py +4 -1
- nautobot/cloud/tests/test_filters.py +5 -4
- nautobot/cloud/views.py +0 -16
- nautobot/core/api/constants.py +11 -0
- nautobot/core/api/filter_backends.py +3 -9
- nautobot/core/api/metadata.py +28 -256
- nautobot/core/api/pagination.py +3 -2
- nautobot/core/api/renderers.py +3 -0
- nautobot/core/api/schema.py +13 -2
- nautobot/core/api/serializers.py +45 -259
- nautobot/core/api/urls.py +3 -4
- nautobot/core/api/utils.py +0 -62
- nautobot/core/api/views.py +99 -157
- nautobot/core/apps/__init__.py +22 -578
- nautobot/core/celery/__init__.py +13 -0
- nautobot/core/celery/schedulers.py +47 -2
- nautobot/core/choices.py +2 -2
- 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/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +57 -9
- nautobot/core/forms/forms.py +33 -2
- nautobot/core/forms/utils.py +2 -1
- nautobot/core/graphql/schema.py +3 -1
- nautobot/core/jobs/__init__.py +24 -3
- nautobot/core/jobs/bulk_actions.py +248 -0
- nautobot/core/jobs/cleanup.py +1 -1
- nautobot/core/management/commands/generate_test_data.py +21 -0
- nautobot/core/middleware.py +16 -0
- nautobot/core/models/fields.py +11 -7
- nautobot/core/settings.py +68 -4
- nautobot/core/settings.yaml +99 -0
- nautobot/core/tables.py +10 -46
- nautobot/core/tasks.py +1 -1
- nautobot/core/templates/about.html +67 -0
- 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/nav_menu.html +2 -1
- nautobot/core/templates/inc/relationships_panel.html +1 -1
- nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
- nautobot/core/templates/inc/tenant_table_row.html +11 -0
- nautobot/core/templates/nautobot_config.py.j2 +13 -0
- nautobot/core/templates/panel_table.html +12 -0
- nautobot/core/templates/utilities/render_jinja2.html +117 -0
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +7 -0
- nautobot/core/templatetags/helpers.py +104 -6
- nautobot/core/templatetags/ui_framework.py +40 -0
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +187 -137
- nautobot/core/testing/context.py +18 -0
- nautobot/core/testing/filters.py +41 -35
- nautobot/core/testing/forms.py +2 -0
- nautobot/core/testing/views.py +65 -148
- nautobot/core/tests/integration/test_view_authentication.py +1 -1
- nautobot/core/tests/nautobot_config.py +198 -0
- nautobot/core/tests/runner.py +2 -2
- nautobot/core/tests/test_api.py +154 -176
- nautobot/core/tests/test_events.py +214 -0
- nautobot/core/tests/test_forms.py +1 -0
- nautobot/core/tests/test_jinja_filters.py +1 -0
- nautobot/core/tests/test_jobs.py +387 -14
- nautobot/core/tests/test_navigations.py +7 -241
- nautobot/core/tests/test_settings_schema.py +7 -0
- nautobot/core/tests/test_tables.py +100 -0
- nautobot/core/tests/test_templatetags_helpers.py +16 -0
- nautobot/core/tests/test_ui.py +150 -0
- nautobot/core/tests/test_utils.py +55 -18
- nautobot/core/tests/test_views.py +124 -5
- 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 +280 -0
- nautobot/core/ui/object_detail.py +1855 -0
- nautobot/core/ui/utils.py +36 -0
- nautobot/core/urls.py +6 -0
- nautobot/core/utils/config.py +30 -3
- nautobot/core/utils/lookup.py +12 -2
- nautobot/core/utils/querysets.py +64 -0
- nautobot/core/utils/requests.py +24 -9
- nautobot/core/views/__init__.py +48 -1
- nautobot/core/views/generic.py +37 -140
- nautobot/core/views/mixins.py +82 -32
- nautobot/core/views/paginator.py +8 -5
- nautobot/core/views/renderers.py +9 -9
- nautobot/core/views/utils.py +11 -0
- nautobot/core/wsgi.py +3 -3
- nautobot/dcim/api/serializers.py +34 -141
- nautobot/dcim/api/urls.py +5 -0
- nautobot/dcim/api/views.py +57 -110
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/choices.py +28 -0
- nautobot/dcim/factory.py +58 -0
- nautobot/dcim/filters/__init__.py +204 -2
- nautobot/dcim/forms.py +219 -9
- 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/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
- nautobot/dcim/models/__init__.py +5 -1
- nautobot/dcim/models/devices.py +180 -2
- nautobot/dcim/models/racks.py +2 -2
- nautobot/dcim/navigation.py +25 -224
- nautobot/dcim/signals.py +44 -0
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +103 -7
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller/base.html +10 -0
- nautobot/dcim/templates/dcim/controller_create.html +2 -7
- nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -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 +4 -10
- nautobot/dcim/templates/dcim/device_edit.html +36 -37
- nautobot/dcim/templates/dcim/interface.html +1 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/location.html +1 -9
- nautobot/dcim/templates/dcim/location_edit.html +1 -7
- nautobot/dcim/templates/dcim/locationtype.html +0 -107
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
- nautobot/dcim/templates/dcim/rack.html +1 -9
- nautobot/dcim/templates/dcim/rack_edit.html +1 -7
- nautobot/dcim/templates/dcim/rackreservation.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
- nautobot/dcim/tests/integration/test_controller.py +62 -0
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
- nautobot/dcim/tests/test_api.py +188 -64
- nautobot/dcim/tests/test_filters.py +171 -76
- nautobot/dcim/tests/test_jobs.py +118 -0
- nautobot/dcim/tests/test_models.py +157 -6
- nautobot/dcim/tests/test_signals.py +1 -0
- nautobot/dcim/tests/test_views.py +118 -88
- nautobot/dcim/urls.py +72 -27
- nautobot/dcim/utils.py +2 -2
- nautobot/dcim/views.py +356 -61
- nautobot/extras/api/serializers.py +39 -18
- nautobot/extras/api/urls.py +4 -0
- nautobot/extras/api/views.py +89 -31
- nautobot/extras/choices.py +13 -0
- nautobot/extras/constants.py +2 -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 +66 -5
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +262 -59
- nautobot/extras/forms/mixins.py +2 -2
- nautobot/extras/graphql/types.py +25 -1
- nautobot/extras/jobs.py +109 -15
- 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/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/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
- nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
- nautobot/extras/models/__init__.py +5 -1
- nautobot/extras/models/change_logging.py +7 -3
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/groups.py +0 -2
- nautobot/extras/models/jobs.py +233 -33
- 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 +54 -19
- nautobot/extras/plugins/marketplace_manifest.yml +455 -0
- nautobot/extras/plugins/tables.py +16 -14
- nautobot/extras/plugins/urls.py +1 -0
- nautobot/extras/plugins/views.py +103 -60
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/signals.py +39 -1
- nautobot/extras/tables.py +37 -1
- nautobot/extras/templates/extras/dynamicgroup.html +1 -9
- 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 +52 -6
- nautobot/extras/templates/extras/job_edit.html +6 -2
- nautobot/extras/templates/extras/job_list.html +2 -7
- nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
- nautobot/extras/templates/extras/marketplace.html +296 -0
- nautobot/extras/templates/extras/plugin_detail.html +32 -15
- nautobot/extras/templates/extras/plugins_list.html +35 -1
- nautobot/extras/templates/extras/plugins_tiles.html +90 -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/singleton.py +16 -0
- nautobot/extras/tests/test_api.py +145 -43
- nautobot/extras/tests/test_context_managers.py +4 -1
- nautobot/extras/tests/test_filters.py +213 -529
- nautobot/extras/tests/test_job_variables.py +73 -152
- nautobot/extras/tests/test_jobs.py +181 -51
- nautobot/extras/tests/test_models.py +61 -6
- nautobot/extras/tests/test_plugins.py +62 -9
- nautobot/extras/tests/test_relationships.py +123 -9
- nautobot/extras/tests/test_utils.py +23 -2
- nautobot/extras/tests/test_views.py +146 -145
- nautobot/extras/tests/test_webhooks.py +2 -1
- nautobot/extras/urls.py +2 -20
- nautobot/extras/utils.py +119 -4
- nautobot/extras/views.py +168 -125
- nautobot/extras/webhooks.py +5 -2
- nautobot/ipam/api/serializers.py +10 -103
- nautobot/ipam/api/views.py +31 -49
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +3 -2
- nautobot/ipam/models.py +10 -12
- nautobot/ipam/navigation.py +0 -90
- nautobot/ipam/tables.py +3 -1
- nautobot/ipam/templates/ipam/ipaddress.html +1 -9
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
- nautobot/ipam/templates/ipam/prefix.html +1 -9
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
- nautobot/ipam/templates/ipam/routetarget.html +0 -28
- nautobot/ipam/templates/ipam/vlan.html +1 -9
- nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
- nautobot/ipam/templates/ipam/vrf.html +0 -47
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
- nautobot/ipam/tests/test_api.py +7 -5
- nautobot/ipam/tests/test_filters.py +39 -119
- nautobot/ipam/tests/test_forms.py +0 -2
- nautobot/ipam/tests/test_models.py +56 -36
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/urls.py +3 -69
- nautobot/ipam/utils/__init__.py +16 -10
- nautobot/ipam/views.py +91 -162
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/css/base.css +38 -3
- nautobot/project-static/docs/404.html +461 -17
- nautobot/project-static/docs/apps/index.html +461 -17
- nautobot/project-static/docs/apps/nautobot-apps.html +462 -19
- nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
- nautobot/project-static/docs/assets/extra.css +5 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +477 -23
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +474 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +790 -289
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +505 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +510 -34
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +471 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +467 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +497 -33
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +523 -75
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +546 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +670 -94
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1030 -177
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +524 -49
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +874 -188
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +955 -235
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +475 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +486 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +661 -99
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +947 -479
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6425 -1234
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +474 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +877 -344
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +828 -171
- nautobot/project-static/docs/development/apps/api/configuration-view.html +461 -17
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/global-search.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/graphql.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +461 -17
- nautobot/project-static/docs/development/apps/api/prometheus.html +461 -17
- nautobot/project-static/docs/development/apps/api/setup.html +465 -153
- nautobot/project-static/docs/development/apps/api/testing.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +741 -128
- nautobot/project-static/docs/development/apps/api/views/base-template.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/index.html +463 -18
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +465 -17
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +491 -17
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/notes.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +467 -19
- nautobot/project-static/docs/development/apps/api/views/urls.html +461 -17
- nautobot/project-static/docs/development/apps/index.html +461 -17
- nautobot/project-static/docs/development/apps/migration/code-updates.html +462 -50
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +462 -18
- nautobot/project-static/docs/development/apps/migration/from-v1.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +464 -20
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +464 -20
- nautobot/project-static/docs/development/core/application-registry.html +461 -17
- nautobot/project-static/docs/development/core/best-practices.html +461 -17
- nautobot/project-static/docs/development/core/bootstrap-ui.html +461 -17
- nautobot/project-static/docs/development/core/caching.html +461 -17
- nautobot/project-static/docs/development/core/controllers.html +463 -17
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +464 -20
- nautobot/project-static/docs/development/core/generic-views.html +461 -17
- nautobot/project-static/docs/development/core/getting-started.html +539 -127
- nautobot/project-static/docs/development/core/homepage.html +472 -28
- nautobot/project-static/docs/development/core/index.html +461 -17
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
- nautobot/project-static/docs/development/core/model-checklist.html +471 -25
- nautobot/project-static/docs/development/core/model-features.html +461 -17
- nautobot/project-static/docs/development/core/natural-keys.html +461 -17
- nautobot/project-static/docs/development/core/navigation-menu.html +478 -24
- nautobot/project-static/docs/development/core/release-checklist.html +478 -46
- nautobot/project-static/docs/development/core/role-internals.html +461 -17
- nautobot/project-static/docs/development/core/settings.html +461 -17
- nautobot/project-static/docs/development/core/style-guide.html +464 -20
- nautobot/project-static/docs/development/core/templates.html +471 -20
- nautobot/project-static/docs/development/core/testing.html +461 -17
- nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
- nautobot/project-static/docs/development/core/user-preferences.html +464 -20
- nautobot/project-static/docs/development/index.html +461 -17
- nautobot/project-static/docs/development/jobs/index.html +499 -19
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +461 -17
- nautobot/project-static/docs/index.html +469 -36
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/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/buttons-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/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/object-fields-panel-example_2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
- nautobot/project-static/docs/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 +467 -21
- nautobot/project-static/docs/overview/design_philosophy.html +461 -17
- nautobot/project-static/docs/release-notes/index.html +483 -20
- nautobot/project-static/docs/release-notes/version-1.0.html +649 -206
- nautobot/project-static/docs/release-notes/version-1.1.html +646 -203
- nautobot/project-static/docs/release-notes/version-1.2.html +721 -278
- nautobot/project-static/docs/release-notes/version-1.3.html +747 -304
- nautobot/project-static/docs/release-notes/version-1.4.html +832 -390
- nautobot/project-static/docs/release-notes/version-1.5.html +1020 -579
- nautobot/project-static/docs/release-notes/version-1.6.html +940 -516
- nautobot/project-static/docs/release-notes/version-2.0.html +943 -502
- nautobot/project-static/docs/release-notes/version-2.1.html +778 -337
- nautobot/project-static/docs/release-notes/version-2.2.html +771 -330
- nautobot/project-static/docs/release-notes/version-2.3.html +914 -471
- nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +342 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +473 -30
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +842 -155
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +474 -27
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +463 -19
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +481 -21
- nautobot/project-static/docs/user-guide/administration/installation/index.html +466 -18
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +462 -18
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/services.html +461 -17
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +461 -17
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +482 -39
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +475 -64
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +475 -64
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +464 -21
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +461 -17
- 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 +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +467 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +497 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +487 -20
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +468 -28
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +464 -20
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +464 -20
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +466 -20
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +464 -20
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
- nautobot/project-static/docs/user-guide/index.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +465 -21
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +470 -21
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +465 -21
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +509 -38
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +492 -21
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +529 -54
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +461 -17
- nautobot/project-static/img/jinja_logo.svg +97 -0
- nautobot/project-static/js/forms.js +6 -1
- nautobot/project-static/js/nav_menu.js +2 -1
- nautobot/tenancy/api/serializers.py +0 -2
- nautobot/tenancy/api/views.py +9 -13
- 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 +35 -24
- nautobot/users/admin.py +2 -0
- nautobot/users/api/views.py +2 -2
- 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/views.py +5 -24
- nautobot/virtualization/filters.py +1 -2
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/navigation.py +0 -48
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
- nautobot/virtualization/tests/test_filters.py +57 -166
- 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 +56 -0
- nautobot/wireless/tests/__init__.py +0 -0
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
- nautobot/wireless/tests/test_api.py +247 -0
- nautobot/wireless/tests/test_filters.py +82 -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 +119 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/METADATA +9 -12
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/RECORD +720 -549
- nautobot/core/utils/navigation.py +0 -54
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/WHEEL +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
nautobot/core/testing/filters.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from __future__ import annotations # python 3.8
|
|
2
|
-
|
|
3
1
|
import random
|
|
4
2
|
import string
|
|
3
|
+
from typing import ClassVar, Iterable
|
|
5
4
|
|
|
6
5
|
from django.contrib.contenttypes.models import ContentType
|
|
7
6
|
from django.db.models import Count, Q, QuerySet
|
|
@@ -21,6 +20,7 @@ from nautobot.core.filters import (
|
|
|
21
20
|
)
|
|
22
21
|
from nautobot.core.models.generics import PrimaryModel
|
|
23
22
|
from nautobot.core.testing import views
|
|
23
|
+
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
24
24
|
from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Tag, Team
|
|
25
25
|
from nautobot.tenancy import models
|
|
26
26
|
|
|
@@ -30,7 +30,7 @@ class FilterTestCases:
|
|
|
30
30
|
class BaseFilterTestCase(views.TestCase):
|
|
31
31
|
"""Base class for testing of FilterSets."""
|
|
32
32
|
|
|
33
|
-
queryset: QuerySet
|
|
33
|
+
queryset: ClassVar[QuerySet]
|
|
34
34
|
|
|
35
35
|
def get_filterset_test_values(self, field_name, queryset=None):
|
|
36
36
|
"""Returns a list of distinct values from the requested queryset field to use in filterset tests.
|
|
@@ -73,7 +73,7 @@ class FilterTestCases:
|
|
|
73
73
|
class FilterTestCase(BaseFilterTestCase):
|
|
74
74
|
"""Add common tests for all FilterSets."""
|
|
75
75
|
|
|
76
|
-
filterset: type[FilterSet]
|
|
76
|
+
filterset: ClassVar[type[FilterSet]]
|
|
77
77
|
|
|
78
78
|
# filter predicate fields that should be excluded from q test case
|
|
79
79
|
exclude_q_filter_predicates = []
|
|
@@ -85,7 +85,13 @@ class FilterTestCases:
|
|
|
85
85
|
# ["filter1"],
|
|
86
86
|
# ["filter2", "field2__name"],
|
|
87
87
|
# ]
|
|
88
|
-
generic_filter_tests
|
|
88
|
+
generic_filter_tests: ClassVar[Iterable]
|
|
89
|
+
|
|
90
|
+
def setUp(self):
|
|
91
|
+
for attr in ["queryset", "filterset", "generic_filter_tests"]:
|
|
92
|
+
if not hasattr(self, attr):
|
|
93
|
+
raise NotImplementedError(f'{self} is missing a value for required attribute "{attr}"')
|
|
94
|
+
super().setUp()
|
|
89
95
|
|
|
90
96
|
def get_q_filter(self):
|
|
91
97
|
"""Helper method to return q filter."""
|
|
@@ -141,15 +147,22 @@ class FilterTestCases:
|
|
|
141
147
|
)
|
|
142
148
|
This expects a field named `devices` on the model and a filter named `devices` on the filterset.
|
|
143
149
|
"""
|
|
150
|
+
if not any(test[0] == "id" for test in self.generic_filter_tests):
|
|
151
|
+
self.generic_filter_tests = (["id"], *self.generic_filter_tests)
|
|
152
|
+
|
|
144
153
|
if getattr(self.queryset.model, "is_contact_associable_model", False):
|
|
145
|
-
for
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
if not any(test[0] == "contacts" for test in self.generic_filter_tests):
|
|
155
|
+
self.generic_filter_tests = (
|
|
156
|
+
*self.generic_filter_tests,
|
|
157
|
+
["contacts", "associated_contacts__contact__name"],
|
|
158
|
+
["contacts", "associated_contacts__contact__id"],
|
|
159
|
+
)
|
|
160
|
+
if not any(test[0] == "teams" for test in self.generic_filter_tests):
|
|
161
|
+
self.generic_filter_tests = (
|
|
162
|
+
*self.generic_filter_tests,
|
|
163
|
+
["teams", "associated_contacts__team__name"],
|
|
164
|
+
["teams", "associated_contacts__team__id"],
|
|
165
|
+
)
|
|
153
166
|
|
|
154
167
|
# Make sure we have at least 3 contacts and 3 teams in the database
|
|
155
168
|
if Contact.objects.count() < 3:
|
|
@@ -177,9 +190,6 @@ class FilterTestCases:
|
|
|
177
190
|
status=Status.objects.get_for_model(ContactAssociation).last(),
|
|
178
191
|
)
|
|
179
192
|
|
|
180
|
-
if not self.generic_filter_tests:
|
|
181
|
-
self.skipTest("No generic_filter_tests defined?")
|
|
182
|
-
|
|
183
193
|
for test in self.generic_filter_tests:
|
|
184
194
|
filter_name = test[0]
|
|
185
195
|
field_name = test[-1] # default to filter_name if a second list item was not supplied
|
|
@@ -238,10 +248,6 @@ class FilterTestCases:
|
|
|
238
248
|
qs_result = self.queryset.filter(tags=tags[0]).filter(tags=tags[1]).distinct()
|
|
239
249
|
self.assertQuerysetEqualAndNotEmpty(filterset_result, qs_result)
|
|
240
250
|
|
|
241
|
-
def test_q_filter_exists(self):
|
|
242
|
-
"""Test the `q` filter exists on a filterset, does not validate the filter works as expected."""
|
|
243
|
-
self.assertIn("q", self.filterset.declared_filters)
|
|
244
|
-
|
|
245
251
|
def _assert_valid_filter_predicates(self, obj, field_name):
|
|
246
252
|
self.assertTrue(
|
|
247
253
|
hasattr(obj, field_name),
|
|
@@ -377,27 +383,27 @@ class FilterTestCases:
|
|
|
377
383
|
filter_field, (ContentTypeFilter, ContentTypeMultipleChoiceFilter, ContentTypeChoiceFilter)
|
|
378
384
|
)
|
|
379
385
|
|
|
386
|
+
# Test cases should just explicitly include `name` as a generic_filter_tests entry
|
|
387
|
+
@class_deprecated_in_favor_of(FilterTestCase) # pylint: disable=undefined-variable
|
|
380
388
|
class NameOnlyFilterTestCase(FilterTestCase):
|
|
381
389
|
"""Add simple tests for filtering by name."""
|
|
382
390
|
|
|
383
|
-
def
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
self.assertTrue(filterset.is_valid())
|
|
388
|
-
self.assertQuerysetEqualAndNotEmpty(
|
|
389
|
-
filterset.qs.order_by("name"), self.queryset.filter(name__in=params["name"]).order_by("name")
|
|
390
|
-
)
|
|
391
|
+
def test_filters_generic(self):
|
|
392
|
+
if not any(test[0] == "name" for test in self.generic_filter_tests):
|
|
393
|
+
self.generic_filter_tests = (["name"], *self.generic_filter_tests)
|
|
394
|
+
super().test_filters_generic()
|
|
391
395
|
|
|
392
|
-
|
|
396
|
+
# Test cases should just explicitly include `name` and `slug` as generic_filter_tests entries
|
|
397
|
+
@class_deprecated_in_favor_of(FilterTestCase) # pylint: disable=undefined-variable
|
|
398
|
+
class NameSlugFilterTestCase(FilterTestCase):
|
|
393
399
|
"""Add simple tests for filtering by name and by slug."""
|
|
394
400
|
|
|
395
|
-
def
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
+
def test_filters_generic(self):
|
|
402
|
+
if not any(test[0] == "slug" for test in self.generic_filter_tests):
|
|
403
|
+
self.generic_filter_tests = (["slug"], *self.generic_filter_tests)
|
|
404
|
+
if not any(test[0] == "name" for test in self.generic_filter_tests):
|
|
405
|
+
self.generic_filter_tests = (["name"], *self.generic_filter_tests)
|
|
406
|
+
super().test_filters_generic()
|
|
401
407
|
|
|
402
408
|
class TenancyFilterTestCaseMixin(views.TestCase):
|
|
403
409
|
"""Add test cases for tenant and tenant-group filters."""
|
nautobot/core/testing/forms.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.test import tag, TestCase
|
|
2
2
|
|
|
3
|
+
from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
|
|
3
4
|
from nautobot.core.forms.fields import DynamicModelChoiceMixin
|
|
4
5
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
5
6
|
|
|
@@ -18,6 +19,7 @@ class FormTestCases:
|
|
|
18
19
|
continue
|
|
19
20
|
with self.subTest(f"Assert {self.form_class.__name__}.{field_name} query_params are valid."):
|
|
20
21
|
query_params_fields = set(field_class.query_params.keys())
|
|
22
|
+
query_params_fields = query_params_fields - set(NON_FILTER_QUERY_PARAMS)
|
|
21
23
|
if not query_params_fields:
|
|
22
24
|
self.skipTest(f"{self.form_class.__name__}.{field_name} has no query_params")
|
|
23
25
|
field_model = field_class.queryset.model
|
nautobot/core/testing/views.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations # python 3.8
|
|
2
|
-
|
|
3
1
|
import contextlib
|
|
4
2
|
import re
|
|
5
3
|
from typing import Optional, Sequence
|
|
@@ -27,14 +25,15 @@ from nautobot.dcim.models.device_components import ComponentModel
|
|
|
27
25
|
from nautobot.extras import choices as extras_choices, models as extras_models, querysets as extras_querysets
|
|
28
26
|
from nautobot.extras.forms import CustomFieldModelFormMixin, RelationshipModelFormMixin
|
|
29
27
|
from nautobot.extras.models import CustomFieldModel, RelationshipModel
|
|
28
|
+
from nautobot.extras.models.jobs import JobResult
|
|
30
29
|
from nautobot.extras.models.mixins import NotesMixin
|
|
31
30
|
from nautobot.ipam.models import Prefix
|
|
32
31
|
from nautobot.users import models as users_models
|
|
33
32
|
|
|
34
33
|
__all__ = (
|
|
35
|
-
"TestCase",
|
|
36
34
|
"ModelTestCase",
|
|
37
35
|
"ModelViewTestCase",
|
|
36
|
+
"TestCase",
|
|
38
37
|
"ViewTestCases",
|
|
39
38
|
)
|
|
40
39
|
|
|
@@ -174,10 +173,7 @@ class ViewTestCases:
|
|
|
174
173
|
instance = self._get_queryset().first()
|
|
175
174
|
|
|
176
175
|
# Add model-level permission
|
|
177
|
-
|
|
178
|
-
obj_perm.save()
|
|
179
|
-
obj_perm.users.add(self.user)
|
|
180
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
176
|
+
self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
|
|
181
177
|
|
|
182
178
|
# Try GET with model-level permission
|
|
183
179
|
response = self.client.get(instance.get_absolute_url())
|
|
@@ -326,10 +322,7 @@ class ViewTestCases:
|
|
|
326
322
|
initial_count = self._get_queryset().count()
|
|
327
323
|
|
|
328
324
|
# Assign unconstrained permission
|
|
329
|
-
|
|
330
|
-
obj_perm.save()
|
|
331
|
-
obj_perm.users.add(self.user)
|
|
332
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
325
|
+
self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
|
|
333
326
|
|
|
334
327
|
# Try GET with model-level permission
|
|
335
328
|
self.assertHttpStatus(self.client.get(self._get_url("add")), 200)
|
|
@@ -436,32 +429,6 @@ class ViewTestCases:
|
|
|
436
429
|
if isinstance(model_class, PrimaryModel):
|
|
437
430
|
self.assertIsNotNone(fields.get("tags"))
|
|
438
431
|
|
|
439
|
-
def test_slug_autocreation(self):
|
|
440
|
-
"""Test that slug is autocreated through ORM."""
|
|
441
|
-
# This really should go on a models test page, but we don't have test structures for models.
|
|
442
|
-
if getattr(self.model, "slug_source", None) is not None:
|
|
443
|
-
obj = self.model.objects.get(**{self.slug_source: self.slug_test_object})
|
|
444
|
-
expected_slug = self.slugify_function(getattr(obj, self.slug_source))
|
|
445
|
-
self.assertEqual(obj.slug, expected_slug)
|
|
446
|
-
|
|
447
|
-
def test_slug_not_modified(self):
|
|
448
|
-
"""Ensure save method does not modify slug that is passed in."""
|
|
449
|
-
# This really should go on a models test page, but we don't have test structures for models.
|
|
450
|
-
if getattr(self.model, "slug_source", None) is not None:
|
|
451
|
-
new_slug_source_value = "kwyjibo"
|
|
452
|
-
|
|
453
|
-
obj = self.model.objects.get(**{self.slug_source: self.slug_test_object})
|
|
454
|
-
expected_slug = self.slugify_function(getattr(obj, self.slug_source))
|
|
455
|
-
# Update slug source field str
|
|
456
|
-
filter_ = self.slug_source + "__exact"
|
|
457
|
-
self.model.objects.filter(**{filter_: self.slug_test_object}).update(
|
|
458
|
-
**{self.slug_source: new_slug_source_value}
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
obj.refresh_from_db()
|
|
462
|
-
self.assertEqual(getattr(obj, self.slug_source), new_slug_source_value)
|
|
463
|
-
self.assertEqual(obj.slug, expected_slug)
|
|
464
|
-
|
|
465
432
|
class EditObjectViewTestCase(ModelViewTestCase):
|
|
466
433
|
"""
|
|
467
434
|
Edit a single existing instance.
|
|
@@ -494,10 +461,7 @@ class ViewTestCases:
|
|
|
494
461
|
instance = self._get_queryset().first()
|
|
495
462
|
|
|
496
463
|
# Assign model-level permission
|
|
497
|
-
|
|
498
|
-
obj_perm.save()
|
|
499
|
-
obj_perm.users.add(self.user)
|
|
500
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
464
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
501
465
|
|
|
502
466
|
# Try GET with model-level permission
|
|
503
467
|
self.assertHttpStatus(self.client.get(self._get_url("edit", instance)), 200)
|
|
@@ -618,10 +582,7 @@ class ViewTestCases:
|
|
|
618
582
|
instance_note_pk_list.append(note.pk)
|
|
619
583
|
|
|
620
584
|
# Assign model-level permission
|
|
621
|
-
|
|
622
|
-
obj_perm.save()
|
|
623
|
-
obj_perm.users.add(self.user)
|
|
624
|
-
obj_perm.object_types.add(assigned_object_type)
|
|
585
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
625
586
|
|
|
626
587
|
# Try GET with model-level permission
|
|
627
588
|
self.assertHttpStatus(self.client.get(self._get_url("delete", instance)), 200)
|
|
@@ -657,10 +618,7 @@ class ViewTestCases:
|
|
|
657
618
|
instance = self.get_deletable_object()
|
|
658
619
|
|
|
659
620
|
# Assign model-level permission
|
|
660
|
-
|
|
661
|
-
obj_perm.save()
|
|
662
|
-
obj_perm.users.add(self.user)
|
|
663
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
621
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
664
622
|
|
|
665
623
|
# Try GET with model-level permission
|
|
666
624
|
self.assertHttpStatus(self.client.get(self._get_url("delete", instance)), 200)
|
|
@@ -865,10 +823,7 @@ class ViewTestCases:
|
|
|
865
823
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
866
824
|
def test_list_objects_with_permission(self):
|
|
867
825
|
# Add model-level permission
|
|
868
|
-
|
|
869
|
-
obj_perm.save()
|
|
870
|
-
obj_perm.users.add(self.user)
|
|
871
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
826
|
+
self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
|
|
872
827
|
|
|
873
828
|
# Try GET with model-level permission
|
|
874
829
|
response = self.client.get(self._get_url("list"))
|
|
@@ -881,11 +836,17 @@ class ViewTestCases:
|
|
|
881
836
|
# Check if breadcrumb is rendered correctly
|
|
882
837
|
self.assertBodyContains(response, f'<a href="{list_url}">{title}</a>', html=True)
|
|
883
838
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
839
|
+
with self.subTest("Assert import-objects URL is absent due to user permissions"):
|
|
840
|
+
self.assertNotIn(
|
|
841
|
+
reverse("extras:job_run_by_class_path", kwargs={"class_path": "nautobot.core.jobs.ImportObjects"}),
|
|
842
|
+
response_body,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
if "example_app" in settings.PLUGINS:
|
|
846
|
+
with self.subTest("Assert example-app banner is present"):
|
|
847
|
+
self.assertIn(
|
|
848
|
+
f"<div>You are viewing a table of {self.model._meta.verbose_name_plural}</div>", response_body
|
|
849
|
+
)
|
|
889
850
|
|
|
890
851
|
return response
|
|
891
852
|
|
|
@@ -992,13 +953,7 @@ class ViewTestCases:
|
|
|
992
953
|
}
|
|
993
954
|
|
|
994
955
|
# Assign non-constrained permission
|
|
995
|
-
|
|
996
|
-
name="Test permission",
|
|
997
|
-
actions=["add"],
|
|
998
|
-
)
|
|
999
|
-
obj_perm.save()
|
|
1000
|
-
obj_perm.users.add(self.user)
|
|
1001
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
956
|
+
self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
|
|
1002
957
|
|
|
1003
958
|
# Bulk create objects
|
|
1004
959
|
response = self.client.post(**request)
|
|
@@ -1052,34 +1007,6 @@ class ViewTestCases:
|
|
|
1052
1007
|
pass
|
|
1053
1008
|
self.assertEqual(matching_count, self.bulk_create_count)
|
|
1054
1009
|
|
|
1055
|
-
class BulkImportObjectsViewTestCase(ModelViewTestCase): # 3.0 TODO: remove this test mixin, no longer relevant.
|
|
1056
|
-
"""
|
|
1057
|
-
Vestigial test case, to be removed in 3.0.
|
|
1058
|
-
|
|
1059
|
-
This is vestigial since the introduction of the ImportObjects system Job to handle bulk-import of all
|
|
1060
|
-
content-types via REST API serializers. The parsing of CSV data by the serializer is exercised by
|
|
1061
|
-
APIViewTestCases.CreateObjectViewTestCase.test_recreate_object_csv(), and the basic operation of the Job is
|
|
1062
|
-
exercised by nautobot.core.tests.test_jobs.
|
|
1063
|
-
"""
|
|
1064
|
-
|
|
1065
|
-
csv_data = ()
|
|
1066
|
-
|
|
1067
|
-
def _get_csv_data(self):
|
|
1068
|
-
return "\n".join(self.csv_data)
|
|
1069
|
-
|
|
1070
|
-
# Just in case Apps are extending any of these tests and calling super() in them.
|
|
1071
|
-
def test_bulk_import_objects_without_permission(self):
|
|
1072
|
-
pass
|
|
1073
|
-
|
|
1074
|
-
def test_bulk_import_objects_with_permission(self):
|
|
1075
|
-
pass
|
|
1076
|
-
|
|
1077
|
-
def test_bulk_import_objects_with_permission_csv_file(self):
|
|
1078
|
-
pass
|
|
1079
|
-
|
|
1080
|
-
def test_bulk_import_objects_with_constrained_permission(self):
|
|
1081
|
-
pass
|
|
1082
|
-
|
|
1083
1010
|
class BulkEditObjectsViewTestCase(ModelViewTestCase):
|
|
1084
1011
|
"""
|
|
1085
1012
|
Edit multiple instances.
|
|
@@ -1094,6 +1021,17 @@ class ViewTestCases:
|
|
|
1094
1021
|
for instance in self._get_queryset().filter(pk__in=pk_list):
|
|
1095
1022
|
self.assertInstanceEqual(instance, self.bulk_edit_data)
|
|
1096
1023
|
|
|
1024
|
+
def validate_redirect_to_job_result(self, response):
|
|
1025
|
+
# Get the last Bulk Edit Objects JobResult created
|
|
1026
|
+
job_result = JobResult.objects.filter(name="Bulk Edit Objects").first()
|
|
1027
|
+
# Assert redirect to Job Results
|
|
1028
|
+
self.assertRedirects(
|
|
1029
|
+
response,
|
|
1030
|
+
reverse("extras:jobresult", args=[job_result.pk]),
|
|
1031
|
+
status_code=302,
|
|
1032
|
+
target_status_code=200,
|
|
1033
|
+
)
|
|
1034
|
+
|
|
1097
1035
|
def test_bulk_edit_objects_without_permission(self):
|
|
1098
1036
|
pk_list = list(self._get_queryset().values_list("pk", flat=True)[:3])
|
|
1099
1037
|
data = {
|
|
@@ -1117,14 +1055,10 @@ class ViewTestCases:
|
|
|
1117
1055
|
data.update(utils.post_data(self.bulk_edit_data))
|
|
1118
1056
|
|
|
1119
1057
|
# Assign model-level permission
|
|
1120
|
-
|
|
1121
|
-
obj_perm.save()
|
|
1122
|
-
obj_perm.users.add(self.user)
|
|
1123
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1058
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1124
1059
|
|
|
1125
|
-
|
|
1126
|
-
self.
|
|
1127
|
-
self.validate_object_data_after_bulk_edit(pk_list)
|
|
1060
|
+
response = self.client.post(self._get_url("bulk_edit"), data)
|
|
1061
|
+
self.validate_redirect_to_job_result(response)
|
|
1128
1062
|
|
|
1129
1063
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1130
1064
|
def test_bulk_edit_form_contains_all_pks(self):
|
|
@@ -1135,10 +1069,7 @@ class ViewTestCases:
|
|
|
1135
1069
|
"_all": "on",
|
|
1136
1070
|
}
|
|
1137
1071
|
# Assign model-level permission
|
|
1138
|
-
|
|
1139
|
-
obj_perm.save()
|
|
1140
|
-
obj_perm.users.add(self.user)
|
|
1141
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1072
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1142
1073
|
|
|
1143
1074
|
# Try POST with model-level permission
|
|
1144
1075
|
response = self.client.post(self._get_url("bulk_edit"), selected_data)
|
|
@@ -1152,7 +1083,7 @@ class ViewTestCases:
|
|
|
1152
1083
|
for pk in pk_list:
|
|
1153
1084
|
self.assertNotIn(str(pk), response_body)
|
|
1154
1085
|
self.assertInHTML(
|
|
1155
|
-
'<input type="hidden" name="_all" value="True" class="form-control"
|
|
1086
|
+
'<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
|
|
1156
1087
|
response_body,
|
|
1157
1088
|
)
|
|
1158
1089
|
|
|
@@ -1188,7 +1119,7 @@ class ViewTestCases:
|
|
|
1188
1119
|
self.assertNotIn(str(third_pk), response_body)
|
|
1189
1120
|
self.assertIn("Editing 2 ", response_body)
|
|
1190
1121
|
self.assertInHTML(
|
|
1191
|
-
'<input type="hidden" name="_all" value="True" class="form-control"
|
|
1122
|
+
'<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
|
|
1192
1123
|
response_body,
|
|
1193
1124
|
)
|
|
1194
1125
|
|
|
@@ -1228,21 +1159,9 @@ class ViewTestCases:
|
|
|
1228
1159
|
|
|
1229
1160
|
# Attempt to bulk edit permitted objects into a non-permitted state
|
|
1230
1161
|
response = self.client.post(self._get_url("bulk_edit"), data)
|
|
1231
|
-
#
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
for instance in self._get_queryset().filter(pk__in=pk_list):
|
|
1235
|
-
self.assertIn(field.value_from_object(instance), values)
|
|
1236
|
-
self.assertNotEqual(field.value_from_object(instance), self.bulk_edit_data[attr_name])
|
|
1237
|
-
|
|
1238
|
-
# Update permission constraints to permit all objects
|
|
1239
|
-
obj_perm.constraints = {"pk__gt": 0}
|
|
1240
|
-
obj_perm.save()
|
|
1241
|
-
|
|
1242
|
-
# Bulk edit permitted objects and expect a redirect back to the list view
|
|
1243
|
-
self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
|
|
1244
|
-
# Assert that the objects were all updated correctly
|
|
1245
|
-
self.validate_object_data_after_bulk_edit(pk_list)
|
|
1162
|
+
# NOTE: There is no way of testing constrained failure as bulk edit is a system Job;
|
|
1163
|
+
# and can only be tested by checking JobLogs.
|
|
1164
|
+
self.validate_redirect_to_job_result(response)
|
|
1246
1165
|
|
|
1247
1166
|
class BulkDeleteObjectsViewTestCase(ModelViewTestCase):
|
|
1248
1167
|
"""
|
|
@@ -1274,7 +1193,6 @@ class ViewTestCases:
|
|
|
1274
1193
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1275
1194
|
def test_bulk_delete_objects_with_permission(self):
|
|
1276
1195
|
pk_list = self.get_deletable_object_pks()
|
|
1277
|
-
initial_count = self._get_queryset().count()
|
|
1278
1196
|
data = {
|
|
1279
1197
|
"pk": pk_list,
|
|
1280
1198
|
"confirm": True,
|
|
@@ -1282,14 +1200,18 @@ class ViewTestCases:
|
|
|
1282
1200
|
}
|
|
1283
1201
|
|
|
1284
1202
|
# Assign unconstrained permission
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1203
|
+
self.add_permissions(
|
|
1204
|
+
f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}", "extras.view_jobresult"
|
|
1205
|
+
)
|
|
1289
1206
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
self.
|
|
1207
|
+
response = self.client.post(self._get_url("bulk_delete"), data)
|
|
1208
|
+
job_result = JobResult.objects.filter(name="Bulk Delete Objects").first()
|
|
1209
|
+
self.assertRedirects(
|
|
1210
|
+
response,
|
|
1211
|
+
reverse("extras:jobresult", args=[job_result.pk]),
|
|
1212
|
+
status_code=302,
|
|
1213
|
+
target_status_code=200,
|
|
1214
|
+
)
|
|
1293
1215
|
|
|
1294
1216
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1295
1217
|
def test_bulk_delete_form_contains_all_objects(self):
|
|
@@ -1301,10 +1223,7 @@ class ViewTestCases:
|
|
|
1301
1223
|
}
|
|
1302
1224
|
|
|
1303
1225
|
# Assign unconstrained permission
|
|
1304
|
-
|
|
1305
|
-
obj_perm.save()
|
|
1306
|
-
obj_perm.users.add(self.user)
|
|
1307
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1226
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
1308
1227
|
|
|
1309
1228
|
# Try POST with the selected data first. Emulating selecting all -> pressing Delete Selected button.
|
|
1310
1229
|
response = self.client.post(self._get_url("bulk_delete"), selected_data)
|
|
@@ -1376,9 +1295,16 @@ class ViewTestCases:
|
|
|
1376
1295
|
obj_perm.constraints = {"pk__isnull": False} # Match a non-existent pk (i.e., allow all)
|
|
1377
1296
|
obj_perm.save()
|
|
1378
1297
|
|
|
1379
|
-
#
|
|
1380
|
-
self.
|
|
1381
|
-
self.
|
|
1298
|
+
# User would be redirected to Job Result therefore user needs to have permission to view Job Result
|
|
1299
|
+
self.add_permissions("extras.view_jobresult")
|
|
1300
|
+
response = self.client.post(self._get_url("bulk_delete"), data)
|
|
1301
|
+
job_result = JobResult.objects.filter(name="Bulk Delete Objects").first()
|
|
1302
|
+
self.assertRedirects(
|
|
1303
|
+
response,
|
|
1304
|
+
reverse("extras:jobresult", args=[job_result.pk]),
|
|
1305
|
+
status_code=302,
|
|
1306
|
+
target_status_code=200,
|
|
1307
|
+
)
|
|
1382
1308
|
|
|
1383
1309
|
class BulkRenameObjectsViewTestCase(ModelViewTestCase):
|
|
1384
1310
|
"""
|
|
@@ -1418,10 +1344,7 @@ class ViewTestCases:
|
|
|
1418
1344
|
data.update(self.rename_data)
|
|
1419
1345
|
|
|
1420
1346
|
# Assign model-level permission
|
|
1421
|
-
|
|
1422
|
-
obj_perm.save()
|
|
1423
|
-
obj_perm.users.add(self.user)
|
|
1424
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1347
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1425
1348
|
|
|
1426
1349
|
# Try POST with model-level permission
|
|
1427
1350
|
self.assertHttpStatus(self.client.post(self._get_url("bulk_rename"), data), 302)
|
|
@@ -1537,10 +1460,7 @@ class ViewTestCases:
|
|
|
1537
1460
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1538
1461
|
def test_bulk_add_component(self):
|
|
1539
1462
|
"""Test bulk-adding this component to devices/virtual-machines."""
|
|
1540
|
-
|
|
1541
|
-
obj_perm.save()
|
|
1542
|
-
obj_perm.users.add(self.user)
|
|
1543
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1463
|
+
self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
|
|
1544
1464
|
|
|
1545
1465
|
initial_count = self._get_queryset().count()
|
|
1546
1466
|
|
|
@@ -1584,10 +1504,7 @@ class ViewTestCases:
|
|
|
1584
1504
|
|
|
1585
1505
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1586
1506
|
def test_bulk_rename(self):
|
|
1587
|
-
|
|
1588
|
-
obj_perm.save()
|
|
1589
|
-
obj_perm.users.add(self.user)
|
|
1590
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
|
1507
|
+
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1591
1508
|
|
|
1592
1509
|
objects = self.selected_objects
|
|
1593
1510
|
pk_list = [obj.pk for obj in objects]
|
|
@@ -14,7 +14,7 @@ class AuthenticationEnforcedTestCase(TestCase):
|
|
|
14
14
|
|
|
15
15
|
def test_all_views_require_authentication(self):
|
|
16
16
|
self.client.logout()
|
|
17
|
-
url_patterns = get_url_patterns()
|
|
17
|
+
url_patterns = get_url_patterns(ignore_redirects=True)
|
|
18
18
|
|
|
19
19
|
for url_pattern in url_patterns:
|
|
20
20
|
with self.subTest(url_pattern=url_pattern):
|