nautobot 2.4.0__py3-none-any.whl → 2.4.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/__init__.py +1 -1
- nautobot/apps/api.py +8 -8
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/events.py +3 -3
- nautobot/apps/factory.py +2 -2
- nautobot/apps/filters.py +1 -1
- nautobot/apps/forms.py +20 -20
- nautobot/apps/graphql.py +2 -2
- nautobot/apps/jobs.py +8 -8
- nautobot/apps/models.py +19 -19
- nautobot/apps/tables.py +1 -1
- nautobot/apps/testing.py +10 -10
- nautobot/apps/ui.py +2 -2
- nautobot/apps/utils.py +7 -7
- nautobot/apps/views.py +7 -7
- nautobot/circuits/api/serializers.py +0 -1
- nautobot/circuits/api/views.py +8 -4
- nautobot/circuits/tables.py +1 -2
- nautobot/circuits/templates/circuits/circuit_create.html +7 -1
- nautobot/circuits/views.py +3 -3
- nautobot/cloud/api/views.py +10 -6
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/views.py +16 -0
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/filter_backends.py +9 -3
- nautobot/core/api/schema.py +2 -13
- nautobot/core/api/serializers.py +34 -40
- nautobot/core/api/views.py +4 -56
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +1 -1
- nautobot/core/choices.py +2 -2
- nautobot/core/events/__init__.py +3 -3
- nautobot/core/filters.py +16 -21
- nautobot/core/fixtures/user-data.json +59 -0
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +11 -14
- nautobot/core/forms/forms.py +2 -33
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +7 -28
- nautobot/core/jobs/cleanup.py +12 -48
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +3 -6
- nautobot/core/settings.py +2 -29
- nautobot/core/settings.yaml +0 -21
- nautobot/core/tables.py +60 -74
- nautobot/core/templates/inc/media.html +0 -3
- nautobot/core/templates/inc/nav_menu.html +0 -1
- nautobot/core/templates/nautobot_config.py.j2 +0 -13
- nautobot/core/templates/search.html +0 -7
- nautobot/core/templates/utilities/render_jinja2.html +1 -1
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +0 -7
- nautobot/core/templatetags/helpers.py +2 -11
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +15 -170
- nautobot/core/testing/filters.py +2 -25
- nautobot/core/testing/forms.py +0 -2
- nautobot/core/testing/mixins.py +2 -7
- nautobot/core/testing/views.py +29 -44
- nautobot/core/tests/integration/test_app_home.py +1 -0
- nautobot/core/tests/integration/test_app_navbar.py +1 -0
- nautobot/core/tests/integration/test_filters.py +2 -0
- nautobot/core/tests/integration/test_home.py +1 -0
- nautobot/core/tests/integration/test_navbar.py +1 -0
- nautobot/core/tests/integration/test_view_authentication.py +0 -1
- nautobot/core/tests/runner.py +1 -1
- nautobot/core/tests/test_api.py +1 -98
- nautobot/core/tests/test_csv.py +3 -25
- nautobot/core/tests/test_forms.py +0 -1
- nautobot/core/tests/test_jobs.py +1 -303
- nautobot/core/tests/test_settings_schema.py +0 -7
- nautobot/core/tests/test_tables.py +0 -100
- nautobot/core/tests/test_utils.py +1 -63
- nautobot/core/tests/test_views.py +3 -30
- nautobot/core/ui/nav.py +0 -1
- nautobot/core/ui/object_detail.py +1 -15
- nautobot/core/urls.py +0 -11
- nautobot/core/utils/lookup.py +8 -11
- nautobot/core/utils/requests.py +9 -24
- nautobot/core/views/__init__.py +0 -42
- nautobot/core/views/generic.py +176 -78
- nautobot/core/views/mixins.py +34 -94
- nautobot/core/views/renderers.py +6 -6
- nautobot/dcim/api/serializers.py +62 -54
- nautobot/dcim/api/views.py +113 -47
- nautobot/dcim/filters/__init__.py +2 -31
- nautobot/dcim/forms.py +17 -36
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/models/__init__.py +1 -1
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +20 -22
- nautobot/dcim/models/devices.py +1 -10
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +5 -6
- nautobot/dcim/models/racks.py +4 -4
- nautobot/dcim/tables/__init__.py +3 -3
- nautobot/dcim/tables/devices.py +5 -7
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller_create.html +7 -1
- nautobot/dcim/templates/dcim/controller_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -5
- nautobot/dcim/templates/dcim/device.html +9 -1
- nautobot/dcim/templates/dcim/device_edit.html +37 -36
- nautobot/dcim/templates/dcim/location.html +9 -1
- nautobot/dcim/templates/dcim/location_edit.html +7 -1
- nautobot/dcim/templates/dcim/rack.html +9 -1
- nautobot/dcim/templates/dcim/rack_edit.html +7 -1
- nautobot/dcim/templates/dcim/rackreservation.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +7 -1
- nautobot/dcim/tests/test_api.py +3 -16
- nautobot/dcim/tests/test_filters.py +0 -33
- nautobot/dcim/tests/test_forms.py +2 -51
- nautobot/dcim/tests/test_graphql.py +0 -52
- nautobot/dcim/tests/test_models.py +5 -34
- nautobot/dcim/tests/test_views.py +83 -21
- nautobot/dcim/views.py +13 -1
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +85 -90
- nautobot/extras/api/views.py +27 -22
- nautobot/extras/constants.py +0 -2
- nautobot/extras/filters/__init__.py +6 -8
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +31 -139
- nautobot/extras/forms/mixins.py +5 -13
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +2 -1
- nautobot/extras/jobs.py +12 -70
- nautobot/extras/managers.py +1 -3
- nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +11 -12
- nautobot/extras/models/groups.py +9 -11
- nautobot/extras/models/jobs.py +4 -23
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/plugins/__init__.py +2 -13
- nautobot/extras/plugins/marketplace_manifest.yml +79 -84
- nautobot/extras/plugins/tables.py +14 -16
- nautobot/extras/plugins/views.py +69 -65
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/tables.py +5 -7
- nautobot/extras/templates/extras/dynamicgroup.html +9 -1
- nautobot/extras/templates/extras/job_detail.html +0 -16
- nautobot/extras/templates/extras/job_edit.html +0 -1
- nautobot/extras/templates/extras/jobqueue_retrieve.html +9 -1
- nautobot/extras/templates/extras/marketplace.html +11 -29
- nautobot/extras/templates/extras/plugin_detail.html +15 -32
- nautobot/extras/templates/extras/plugins_tiles.html +10 -21
- nautobot/extras/test_jobs/api_test_job.py +1 -1
- nautobot/extras/test_jobs/atomic_transaction.py +2 -2
- nautobot/extras/test_jobs/dry_run.py +1 -1
- nautobot/extras/test_jobs/fail.py +5 -5
- nautobot/extras/test_jobs/file_output.py +1 -1
- nautobot/extras/test_jobs/file_upload_fail.py +1 -1
- nautobot/extras/test_jobs/file_upload_pass.py +1 -1
- nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
- nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
- nautobot/extras/test_jobs/log_redaction.py +1 -1
- nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
- nautobot/extras/test_jobs/modify_db.py +1 -1
- nautobot/extras/test_jobs/object_var_optional.py +1 -1
- nautobot/extras/test_jobs/object_var_required.py +1 -1
- nautobot/extras/test_jobs/object_vars.py +1 -1
- nautobot/extras/test_jobs/pass.py +3 -3
- nautobot/extras/test_jobs/profiling.py +1 -1
- nautobot/extras/test_jobs/relative_import.py +3 -3
- nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
- nautobot/extras/test_jobs/task_queues.py +1 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
- nautobot/extras/tests/test_api.py +13 -13
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +1 -2
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +6 -6
- nautobot/extras/tests/test_forms.py +1 -20
- nautobot/extras/tests/test_jobs.py +19 -160
- nautobot/extras/tests/test_models.py +10 -10
- nautobot/extras/tests/test_plugins.py +9 -62
- nautobot/extras/tests/test_relationships.py +9 -120
- nautobot/extras/tests/test_views.py +191 -52
- nautobot/extras/utils.py +2 -3
- nautobot/extras/views.py +98 -30
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +33 -41
- nautobot/ipam/api/views.py +117 -68
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +2 -3
- nautobot/ipam/lookups.py +62 -101
- nautobot/ipam/models.py +16 -66
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +7 -23
- nautobot/ipam/templates/ipam/ipaddress.html +9 -1
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +7 -1
- nautobot/ipam/templates/ipam/ipaddress_edit.html +7 -1
- nautobot/ipam/templates/ipam/prefix.html +9 -1
- nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
- nautobot/ipam/templates/ipam/vlan.html +9 -1
- nautobot/ipam/templates/ipam/vlan_edit.html +7 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +7 -1
- nautobot/ipam/tests/test_api.py +3 -416
- nautobot/ipam/tests/test_forms.py +47 -49
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +34 -95
- nautobot/ipam/tests/test_querysets.py +1 -63
- nautobot/ipam/tests/test_views.py +0 -3
- nautobot/ipam/utils/__init__.py +6 -36
- nautobot/ipam/views.py +87 -61
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +2 -40
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/docs/404.html +4 -46
- nautobot/project-static/docs/apps/index.html +4 -46
- nautobot/project-static/docs/apps/nautobot-apps.html +6 -47
- nautobot/project-static/docs/assets/_mkdocstrings.css +1 -25
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -62
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +122 -374
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -90
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +21 -95
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +6 -53
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +5 -52
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +17 -79
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +28 -102
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +21 -108
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +38 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +65 -239
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +165 -581
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +36 -109
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +167 -453
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +211 -493
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +8 -60
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +15 -71
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +55 -407
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +205 -585
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +412 -858
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +186 -448
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +147 -365
- nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -46
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +7 -68
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -46
- nautobot/project-static/docs/development/apps/api/prometheus.html +4 -46
- nautobot/project-static/docs/development/apps/api/setup.html +4 -46
- nautobot/project-static/docs/development/apps/api/testing.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/notes.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +6 -52
- nautobot/project-static/docs/development/apps/api/views/urls.html +4 -46
- nautobot/project-static/docs/development/apps/index.html +4 -46
- nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +8 -50
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +14 -211
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -46
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -46
- nautobot/project-static/docs/development/core/application-registry.html +4 -46
- nautobot/project-static/docs/development/core/best-practices.html +4 -46
- nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -46
- nautobot/project-static/docs/development/core/caching.html +4 -46
- nautobot/project-static/docs/development/core/controllers.html +4 -46
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +74 -73
- nautobot/project-static/docs/development/core/generic-views.html +4 -46
- nautobot/project-static/docs/development/core/getting-started.html +224 -249
- nautobot/project-static/docs/development/core/homepage.html +7 -49
- nautobot/project-static/docs/development/core/index.html +4 -46
- nautobot/project-static/docs/development/core/{minikube-dev-environment-for-k8s-jobs.html → local-k8s.html} +168 -469
- nautobot/project-static/docs/development/core/model-checklist.html +12 -56
- nautobot/project-static/docs/development/core/model-features.html +4 -46
- nautobot/project-static/docs/development/core/natural-keys.html +4 -46
- nautobot/project-static/docs/development/core/navigation-menu.html +4 -46
- nautobot/project-static/docs/development/core/release-checklist.html +7 -49
- nautobot/project-static/docs/development/core/role-internals.html +4 -46
- nautobot/project-static/docs/development/core/settings.html +4 -46
- nautobot/project-static/docs/development/core/style-guide.html +7 -49
- nautobot/project-static/docs/development/core/templates.html +4 -46
- nautobot/project-static/docs/development/core/testing.html +4 -46
- nautobot/project-static/docs/development/core/ui-component-framework.html +273 -369
- nautobot/project-static/docs/development/core/user-preferences.html +4 -46
- nautobot/project-static/docs/development/index.html +4 -46
- nautobot/project-static/docs/development/jobs/index.html +122 -216
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -46
- nautobot/project-static/docs/index.html +23 -54
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +7 -47
- nautobot/project-static/docs/overview/design_philosophy.html +4 -46
- nautobot/project-static/docs/release-notes/index.html +12 -52
- nautobot/project-static/docs/release-notes/version-1.0.html +193 -234
- nautobot/project-static/docs/release-notes/version-1.1.html +190 -231
- nautobot/project-static/docs/release-notes/version-1.2.html +265 -306
- nautobot/project-static/docs/release-notes/version-1.3.html +291 -332
- nautobot/project-static/docs/release-notes/version-1.4.html +377 -417
- nautobot/project-static/docs/release-notes/version-1.5.html +566 -605
- nautobot/project-static/docs/release-notes/version-1.6.html +447 -904
- nautobot/project-static/docs/release-notes/version-2.0.html +489 -528
- nautobot/project-static/docs/release-notes/version-2.1.html +324 -363
- nautobot/project-static/docs/release-notes/version-2.2.html +317 -356
- nautobot/project-static/docs/release-notes/version-2.3.html +352 -997
- nautobot/project-static/docs/release-notes/version-2.4.html +101 -417
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +287 -295
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +6 -48
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +8 -110
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +6 -48
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +8 -66
- nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +5 -47
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +8 -49
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +12 -50
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +7 -49
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +7 -51
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +7 -49
- nautobot/project-static/docs/user-guide/index.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +8 -50
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +7 -50
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +7 -49
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +5 -47
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -94
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +5 -74
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -46
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +13 -9
- nautobot/tenancy/views.py +2 -4
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +4 -5
- nautobot/users/api/views.py +3 -3
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/api/views.py +24 -5
- nautobot/virtualization/filters.py +3 -20
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +7 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +9 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +8 -2
- nautobot/virtualization/tests/test_filters.py +0 -17
- nautobot/wireless/filters.py +2 -2
- nautobot/wireless/forms.py +1 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +9 -1
- nautobot/wireless/tests/test_filters.py +1 -29
- nautobot/wireless/views.py +10 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +6 -6
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +543 -591
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
- nautobot/core/api/constants.py +0 -11
- nautobot/core/jobs/bulk_actions.py +0 -248
- nautobot/core/templates/about.html +0 -67
- nautobot/core/templates/inc/tenancy_form_panel.html +0 -9
- nautobot/core/templates/inc/tenant_table_row.html +0 -11
- nautobot/core/utils/querysets.py +0 -64
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +0 -25
- nautobot/dcim/tests/integration/test_controller.py +0 -62
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +0 -71
- nautobot/dcim/tests/test_jobs.py +0 -118
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +0 -22
- nautobot/extras/migrations/0121_alter_team_contacts.py +0 -17
- nautobot/extras/test_jobs/singleton.py +0 -16
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -9444
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -9722
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +0 -42
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/core/testing/api.py
CHANGED
|
@@ -5,13 +5,11 @@ from typing import Optional, Sequence, Union
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
7
7
|
from django.contrib.contenttypes.models import ContentType
|
|
8
|
-
from django.db import connections, DEFAULT_DB_ALIAS
|
|
9
8
|
from django.db.models import ForeignKey, ManyToManyField, QuerySet
|
|
10
9
|
from django.test import override_settings, tag
|
|
11
|
-
from django.test.utils import CaptureQueriesContext
|
|
12
10
|
from django.urls import reverse
|
|
13
11
|
from django.utils.text import slugify
|
|
14
|
-
from rest_framework import
|
|
12
|
+
from rest_framework import status
|
|
15
13
|
from rest_framework.relations import ManyRelatedField
|
|
16
14
|
from rest_framework.test import APITransactionTestCase as _APITransactionTestCase
|
|
17
15
|
|
|
@@ -96,15 +94,6 @@ class APITestCase(views.ModelTestCase):
|
|
|
96
94
|
for verboten in self.VERBOTEN_STRINGS:
|
|
97
95
|
self.assertNotIn(verboten, response_raw_content)
|
|
98
96
|
|
|
99
|
-
def get_m2m_fields(self):
|
|
100
|
-
"""Get serializer field names that are many-to-many or one-to-many and thus affected by ?exclude_m2m=true."""
|
|
101
|
-
serializer_class = get_serializer_for_model(self.model)
|
|
102
|
-
m2m_fields = []
|
|
103
|
-
for field_name, field_instance in serializer_class().fields.items():
|
|
104
|
-
if isinstance(field_instance, (serializers.ManyRelatedField, serializers.ListSerializer)):
|
|
105
|
-
m2m_fields.append(field_name)
|
|
106
|
-
return m2m_fields
|
|
107
|
-
|
|
108
97
|
|
|
109
98
|
@tag("unit")
|
|
110
99
|
class APIViewTestCases:
|
|
@@ -242,7 +231,7 @@ class APIViewTestCases:
|
|
|
242
231
|
def get_depth_fields(self):
|
|
243
232
|
"""Get a list of model fields that could be tested with the ?depth query parameter"""
|
|
244
233
|
depth_fields = []
|
|
245
|
-
for field in self.model._meta.
|
|
234
|
+
for field in self.model._meta.fields:
|
|
246
235
|
if not field.name.startswith("_"):
|
|
247
236
|
if isinstance(field, (ForeignKey, GenericForeignKey, ManyToManyField, core_fields.TagsField)) and (
|
|
248
237
|
# we represent content-types as "app_label.modelname" rather than as FKs
|
|
@@ -251,9 +240,6 @@ class APIViewTestCases:
|
|
|
251
240
|
and not (field.name == "user" and self.model == users_models.Token)
|
|
252
241
|
):
|
|
253
242
|
depth_fields.append(field.name)
|
|
254
|
-
serializer_class = get_serializer_for_model(self.model)
|
|
255
|
-
serializer = serializer_class()
|
|
256
|
-
depth_fields = [field_name for field_name in depth_fields if field_name in serializer.fields]
|
|
257
243
|
return depth_fields
|
|
258
244
|
|
|
259
245
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
@@ -283,12 +269,9 @@ class APIViewTestCases:
|
|
|
283
269
|
GET a list of objects using the "?depth=0" parameter.
|
|
284
270
|
"""
|
|
285
271
|
depth_fields = self.get_depth_fields()
|
|
286
|
-
m2m_fields = self.get_m2m_fields()
|
|
287
272
|
self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
response = self.client.get(list_url, **self.header)
|
|
291
|
-
base_num_queries = len(cqc)
|
|
273
|
+
url = f"{self._get_list_url()}?depth=0"
|
|
274
|
+
response = self.client.get(url, **self.header)
|
|
292
275
|
|
|
293
276
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
294
277
|
self.assertIsInstance(response.data, dict)
|
|
@@ -297,23 +280,15 @@ class APIViewTestCases:
|
|
|
297
280
|
self.assert_no_verboten_content(response)
|
|
298
281
|
|
|
299
282
|
for response_data in response.data["results"]:
|
|
300
|
-
for field in m2m_fields:
|
|
301
|
-
self.assertIn(field, response_data)
|
|
302
|
-
self.assertIsInstance(response_data[field], list)
|
|
303
283
|
for field in depth_fields:
|
|
304
284
|
self.assertIn(field, response_data)
|
|
305
285
|
if isinstance(response_data[field], list):
|
|
306
286
|
for entry in response_data[field]:
|
|
307
287
|
self.assertIsInstance(entry, dict)
|
|
308
|
-
|
|
309
|
-
self.assertIsInstance(entry["id"], int)
|
|
310
|
-
else:
|
|
311
|
-
self.assertTrue(is_uuid(entry["id"]))
|
|
312
|
-
self.assertEqual(len(entry.keys()), 3) # just id/object_type/url
|
|
288
|
+
self.assertTrue(is_uuid(entry["id"]))
|
|
313
289
|
else:
|
|
314
290
|
if response_data[field] is not None:
|
|
315
291
|
self.assertIsInstance(response_data[field], dict)
|
|
316
|
-
self.assertEqual(len(response_data[field].keys()), 3) # just id/object_type/url
|
|
317
292
|
url = response_data[field]["url"]
|
|
318
293
|
pk = response_data[field]["id"]
|
|
319
294
|
object_type = response_data[field]["object_type"]
|
|
@@ -321,67 +296,22 @@ class APIViewTestCases:
|
|
|
321
296
|
# URL ending in the UUID of the relevant object:
|
|
322
297
|
# http://nautobot.example.com/api/circuits/providers/<uuid>/
|
|
323
298
|
# ^^^^^^
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
self.assertIsInstance(pk, int)
|
|
327
|
-
else:
|
|
328
|
-
self.assertTrue(is_uuid(url.split("/")[-2]))
|
|
329
|
-
self.assertTrue(is_uuid(pk))
|
|
299
|
+
self.assertTrue(is_uuid(url.split("/")[-2]))
|
|
300
|
+
self.assertTrue(is_uuid(pk))
|
|
330
301
|
|
|
331
302
|
with self.subTest(f"Assert object_type {object_type} is valid"):
|
|
332
303
|
app_label, model_name = object_type.split(".")
|
|
333
304
|
ContentType.objects.get(app_label=app_label, model=model_name)
|
|
334
305
|
|
|
335
|
-
list_url += "&exclude_m2m=true"
|
|
336
|
-
with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
|
|
337
|
-
response = self.client.get(list_url, **self.header)
|
|
338
|
-
|
|
339
|
-
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
340
|
-
self.assertIsInstance(response.data, dict)
|
|
341
|
-
self.assertIn("results", response.data)
|
|
342
|
-
self.assert_no_verboten_content(response)
|
|
343
|
-
|
|
344
|
-
if m2m_fields:
|
|
345
|
-
if self.model._meta.app_label in [
|
|
346
|
-
"circuits",
|
|
347
|
-
"cloud",
|
|
348
|
-
"dcim",
|
|
349
|
-
"extras",
|
|
350
|
-
"ipam",
|
|
351
|
-
"tenancy",
|
|
352
|
-
"users",
|
|
353
|
-
"virtualization",
|
|
354
|
-
"wireless",
|
|
355
|
-
]:
|
|
356
|
-
self.assertLess(
|
|
357
|
-
len(cqc), base_num_queries, "Number of queries did not decrease with ?exclude_m2m=true"
|
|
358
|
-
)
|
|
359
|
-
else:
|
|
360
|
-
# Less strict check for non-core APIs
|
|
361
|
-
self.assertLessEqual(
|
|
362
|
-
len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true"
|
|
363
|
-
)
|
|
364
|
-
else:
|
|
365
|
-
# No M2M fields to exclude
|
|
366
|
-
self.assertLessEqual(len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true")
|
|
367
|
-
|
|
368
|
-
for response_data in response.data["results"]:
|
|
369
|
-
for field in m2m_fields:
|
|
370
|
-
self.assertNotIn(field, response_data)
|
|
371
|
-
# TODO: we should assert that all other fields are still present, but there's a few corner cases...
|
|
372
|
-
|
|
373
306
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
374
307
|
def test_list_objects_depth_1(self):
|
|
375
308
|
"""
|
|
376
309
|
GET a list of objects using the "?depth=1" parameter.
|
|
377
310
|
"""
|
|
378
311
|
depth_fields = self.get_depth_fields()
|
|
379
|
-
m2m_fields = self.get_m2m_fields()
|
|
380
312
|
self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
response = self.client.get(list_url, **self.header)
|
|
384
|
-
base_num_queries = len(cqc)
|
|
313
|
+
url = f"{self._get_list_url()}?depth=1"
|
|
314
|
+
response = self.client.get(url, **self.header)
|
|
385
315
|
|
|
386
316
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
387
317
|
self.assertIsInstance(response.data, dict)
|
|
@@ -390,65 +320,16 @@ class APIViewTestCases:
|
|
|
390
320
|
self.assert_no_verboten_content(response)
|
|
391
321
|
|
|
392
322
|
for response_data in response.data["results"]:
|
|
393
|
-
for field in m2m_fields:
|
|
394
|
-
self.assertIn(field, response_data)
|
|
395
|
-
self.assertIsInstance(response_data[field], list)
|
|
396
323
|
for field in depth_fields:
|
|
397
324
|
self.assertIn(field, response_data)
|
|
398
325
|
if isinstance(response_data[field], list):
|
|
399
326
|
for entry in response_data[field]:
|
|
400
327
|
self.assertIsInstance(entry, dict)
|
|
401
|
-
|
|
402
|
-
self.assertIsInstance(entry["id"], int)
|
|
403
|
-
else:
|
|
404
|
-
self.assertTrue(is_uuid(entry["id"]))
|
|
405
|
-
self.assertGreater(len(entry.keys()), 3, entry) # not just id/object_type/url!
|
|
328
|
+
self.assertTrue(is_uuid(entry["id"]))
|
|
406
329
|
else:
|
|
407
330
|
if response_data[field] is not None:
|
|
408
331
|
self.assertIsInstance(response_data[field], dict)
|
|
409
|
-
|
|
410
|
-
self.assertIsInstance(response_data[field]["id"], int)
|
|
411
|
-
else:
|
|
412
|
-
self.assertTrue(is_uuid(response_data[field]["id"]))
|
|
413
|
-
self.assertGreater(len(response_data[field].keys()), 3, response_data[field])
|
|
414
|
-
|
|
415
|
-
list_url += "&exclude_m2m=true"
|
|
416
|
-
with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
|
|
417
|
-
response = self.client.get(list_url, **self.header)
|
|
418
|
-
|
|
419
|
-
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
420
|
-
self.assertIsInstance(response.data, dict)
|
|
421
|
-
self.assertIn("results", response.data)
|
|
422
|
-
self.assert_no_verboten_content(response)
|
|
423
|
-
|
|
424
|
-
if m2m_fields:
|
|
425
|
-
if self.model._meta.app_label in [
|
|
426
|
-
"circuits",
|
|
427
|
-
"cloud",
|
|
428
|
-
"dcim",
|
|
429
|
-
"extras",
|
|
430
|
-
"ipam",
|
|
431
|
-
"tenancy",
|
|
432
|
-
"users",
|
|
433
|
-
"virtualization",
|
|
434
|
-
"wireless",
|
|
435
|
-
]:
|
|
436
|
-
self.assertLess(
|
|
437
|
-
len(cqc), base_num_queries, "Number of queries did not decrease with ?exclude_m2m=true"
|
|
438
|
-
)
|
|
439
|
-
else:
|
|
440
|
-
# Less strict check for non-core APIs
|
|
441
|
-
self.assertLessEqual(
|
|
442
|
-
len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true"
|
|
443
|
-
)
|
|
444
|
-
else:
|
|
445
|
-
# No M2M fields to exclude
|
|
446
|
-
self.assertLessEqual(len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true")
|
|
447
|
-
|
|
448
|
-
for response_data in response.data["results"]:
|
|
449
|
-
for field in m2m_fields:
|
|
450
|
-
self.assertNotIn(field, response_data)
|
|
451
|
-
# TODO: we should assert that all other fields are still present, but there's a few corner cases...
|
|
332
|
+
self.assertTrue(is_uuid(response_data[field]["id"]))
|
|
452
333
|
|
|
453
334
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
454
335
|
def test_list_objects_without_permission(self):
|
|
@@ -774,11 +655,7 @@ class APIViewTestCases:
|
|
|
774
655
|
self.assertHttpStatus(response, status.HTTP_201_CREATED, csv_data)
|
|
775
656
|
# Note that create via CSV is always treated as a bulk-create, and so the response is always a list of dicts
|
|
776
657
|
new_instance = self._get_queryset().get(pk=response.data[0]["id"])
|
|
777
|
-
|
|
778
|
-
self.assertNotEqual(new_instance.pk, orig_pk)
|
|
779
|
-
else:
|
|
780
|
-
# for our non-integer PKs, we're expecting the creation to respect the requested PK
|
|
781
|
-
self.assertEqual(new_instance.pk, orig_pk)
|
|
658
|
+
self.assertNotEqual(new_instance.pk, orig_pk)
|
|
782
659
|
|
|
783
660
|
new_serializer = serializer_class(new_instance, context={"request": None})
|
|
784
661
|
new_data = new_serializer.data
|
|
@@ -860,7 +737,7 @@ class APIViewTestCases:
|
|
|
860
737
|
|
|
861
738
|
def strip_serialized_object(this_object):
|
|
862
739
|
"""
|
|
863
|
-
|
|
740
|
+
Only here to work around acceptable differences in PATCH response vs GET response which are known bugs.
|
|
864
741
|
"""
|
|
865
742
|
# Work around for https://github.com/nautobot/nautobot/issues/3321
|
|
866
743
|
this_object.pop("last_updated", None)
|
|
@@ -869,12 +746,6 @@ class APIViewTestCases:
|
|
|
869
746
|
this_object.pop("config_context", None)
|
|
870
747
|
this_object.pop("relationships", None)
|
|
871
748
|
|
|
872
|
-
serializer = get_serializer_for_model(self.model)()
|
|
873
|
-
for field_name, field_instance in serializer.fields.items():
|
|
874
|
-
if field_instance.read_only:
|
|
875
|
-
# Likely a derived field, might change as a consequence of other data updates
|
|
876
|
-
this_object.pop(field_name, None)
|
|
877
|
-
|
|
878
749
|
for value in this_object.values():
|
|
879
750
|
if isinstance(value, dict):
|
|
880
751
|
strip_serialized_object(value)
|
|
@@ -918,7 +789,7 @@ class APIViewTestCases:
|
|
|
918
789
|
self.assertEqual(initial_serialized_object, serialized_object)
|
|
919
790
|
|
|
920
791
|
# Verify ObjectChange creation -- yes, even though nothing actually changed
|
|
921
|
-
#
|
|
792
|
+
# This may change (hah) at some point -- see https://github.com/nautobot/nautobot/issues/3321
|
|
922
793
|
if hasattr(self.model, "to_objectchange"):
|
|
923
794
|
objectchanges = lookup.get_changes_for_model(instance)
|
|
924
795
|
self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
|
|
@@ -927,16 +798,10 @@ class APIViewTestCases:
|
|
|
927
798
|
# Verify that a PATCH with some data updates that data correctly.
|
|
928
799
|
response = self.client.patch(url, update_data, format="json", **self.header)
|
|
929
800
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
930
|
-
serialized_object = response.json()
|
|
931
|
-
strip_serialized_object(serialized_object)
|
|
932
801
|
# Check for unexpected side effects on fields we DIDN'T intend to update
|
|
933
802
|
for field in initial_serialized_object:
|
|
934
803
|
if field not in update_data:
|
|
935
|
-
self.assertEqual(
|
|
936
|
-
initial_serialized_object[field],
|
|
937
|
-
serialized_object[field],
|
|
938
|
-
f"data changed unexpectedly for field '{field}'",
|
|
939
|
-
)
|
|
804
|
+
self.assertEqual(initial_serialized_object[field], serialized_object[field])
|
|
940
805
|
instance.refresh_from_db()
|
|
941
806
|
self.assertInstanceEqual(instance, update_data, exclude=self.validation_excluded_fields, api=True)
|
|
942
807
|
|
|
@@ -945,26 +810,6 @@ class APIViewTestCases:
|
|
|
945
810
|
objectchanges = lookup.get_changes_for_model(instance)
|
|
946
811
|
self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
|
|
947
812
|
|
|
948
|
-
# Verify that a PATCH with ?exclude_m2m=true correctly excludes many-to-many fields from the response
|
|
949
|
-
# This also doubles as a test for idempotence of the PATCH request.
|
|
950
|
-
response = self.client.patch(url + "?exclude_m2m=true", update_data, format="json", **self.header)
|
|
951
|
-
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
952
|
-
m2m_fields = self.get_m2m_fields()
|
|
953
|
-
serialized_object = response.json()
|
|
954
|
-
strip_serialized_object(serialized_object)
|
|
955
|
-
for field in m2m_fields:
|
|
956
|
-
self.assertNotIn(field, serialized_object)
|
|
957
|
-
# Check for unexpected side effects on fields we DIDN'T intend to update
|
|
958
|
-
for field in initial_serialized_object:
|
|
959
|
-
if field not in update_data and field not in m2m_fields:
|
|
960
|
-
self.assertEqual(
|
|
961
|
-
initial_serialized_object[field],
|
|
962
|
-
serialized_object[field],
|
|
963
|
-
f"data changed unexpectedly for field '{field}'",
|
|
964
|
-
)
|
|
965
|
-
instance.refresh_from_db()
|
|
966
|
-
self.assertInstanceEqual(instance, update_data, exclude=self.validation_excluded_fields, api=True)
|
|
967
|
-
|
|
968
813
|
def test_get_put_round_trip(self):
|
|
969
814
|
"""GET and then PUT an object and verify that it's accepted and unchanged."""
|
|
970
815
|
self.maxDiff = None
|
nautobot/core/testing/filters.py
CHANGED
|
@@ -30,8 +30,6 @@ class FilterTestCases:
|
|
|
30
30
|
class BaseFilterTestCase(views.TestCase):
|
|
31
31
|
"""Base class for testing of FilterSets."""
|
|
32
32
|
|
|
33
|
-
queryset: ClassVar[QuerySet]
|
|
34
|
-
|
|
35
33
|
def get_filterset_test_values(self, field_name, queryset=None):
|
|
36
34
|
"""Returns a list of distinct values from the requested queryset field to use in filterset tests.
|
|
37
35
|
|
|
@@ -73,7 +71,8 @@ class FilterTestCases:
|
|
|
73
71
|
class FilterTestCase(BaseFilterTestCase):
|
|
74
72
|
"""Add common tests for all FilterSets."""
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
queryset: ClassVar[QuerySet]
|
|
75
|
+
filterset: ClassVar[FilterSet]
|
|
77
76
|
|
|
78
77
|
# filter predicate fields that should be excluded from q test case
|
|
79
78
|
exclude_q_filter_predicates = []
|
|
@@ -97,28 +96,6 @@ class FilterTestCases:
|
|
|
97
96
|
"""Helper method to return q filter."""
|
|
98
97
|
return self.filterset.declared_filters["q"].filter_predicates
|
|
99
98
|
|
|
100
|
-
def test_id(self):
|
|
101
|
-
"""Verify that the filterset supports filtering by id with only lookup `__n`."""
|
|
102
|
-
with self.subTest("Assert `id`"):
|
|
103
|
-
params = {"id": list(self.queryset.values_list("pk", flat=True)[:2])}
|
|
104
|
-
expected_queryset = self.queryset.filter(id__in=params["id"])
|
|
105
|
-
filterset = self.filterset(params, self.queryset)
|
|
106
|
-
self.assertTrue(filterset.is_valid())
|
|
107
|
-
self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
|
|
108
|
-
|
|
109
|
-
with self.subTest("Assert negate lookup"):
|
|
110
|
-
params = {"id__n": list(self.queryset.values_list("pk", flat=True)[:2])}
|
|
111
|
-
expected_queryset = self.queryset.exclude(id__in=params["id__n"])
|
|
112
|
-
filterset = self.filterset(params, self.queryset)
|
|
113
|
-
self.assertTrue(filterset.is_valid())
|
|
114
|
-
self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
|
|
115
|
-
|
|
116
|
-
with self.subTest("Assert invalid lookup"):
|
|
117
|
-
params = {"id__in": list(self.queryset.values_list("pk", flat=True)[:2])}
|
|
118
|
-
filterset = self.filterset(params, self.queryset)
|
|
119
|
-
self.assertFalse(filterset.is_valid())
|
|
120
|
-
self.assertIn("Unknown filter field", filterset.errors.as_text())
|
|
121
|
-
|
|
122
99
|
def test_invalid_filter(self):
|
|
123
100
|
"""Verify that the filterset reports as invalid when initialized with an unsupported filter parameter."""
|
|
124
101
|
params = {"ice_cream_flavor": ["chocolate"]}
|
nautobot/core/testing/forms.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from django.test import tag, TestCase
|
|
2
2
|
|
|
3
|
-
from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
|
|
4
3
|
from nautobot.core.forms.fields import DynamicModelChoiceMixin
|
|
5
4
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
6
5
|
|
|
@@ -19,7 +18,6 @@ class FormTestCases:
|
|
|
19
18
|
continue
|
|
20
19
|
with self.subTest(f"Assert {self.form_class.__name__}.{field_name} query_params are valid."):
|
|
21
20
|
query_params_fields = set(field_class.query_params.keys())
|
|
22
|
-
query_params_fields = query_params_fields - set(NON_FILTER_QUERY_PARAMS)
|
|
23
21
|
if not query_params_fields:
|
|
24
22
|
self.skipTest(f"{self.form_class.__name__}.{field_name} has no query_params")
|
|
25
23
|
field_model = field_class.queryset.model
|
nautobot/core/testing/mixins.py
CHANGED
|
@@ -144,18 +144,13 @@ class NautobotTestCaseMixin:
|
|
|
144
144
|
# Permissions management
|
|
145
145
|
#
|
|
146
146
|
|
|
147
|
-
def add_permissions(self, *names
|
|
147
|
+
def add_permissions(self, *names):
|
|
148
148
|
"""
|
|
149
149
|
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
|
|
150
|
-
Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.
|
|
151
|
-
|
|
152
|
-
Examples:
|
|
153
|
-
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup")
|
|
154
|
-
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup", constraints={"pk": "uuid-1234"})
|
|
155
150
|
"""
|
|
156
151
|
for name in names:
|
|
157
152
|
ct, action = permissions.resolve_permission_ct(name)
|
|
158
|
-
obj_perm = users_models.ObjectPermission(name=name, actions=[action]
|
|
153
|
+
obj_perm = users_models.ObjectPermission(name=name, actions=[action])
|
|
159
154
|
obj_perm.save()
|
|
160
155
|
obj_perm.users.add(self.user)
|
|
161
156
|
obj_perm.object_types.add(ct)
|
nautobot/core/testing/views.py
CHANGED
|
@@ -21,19 +21,17 @@ from nautobot.core.models.tree_queries import TreeModel
|
|
|
21
21
|
from nautobot.core.templatetags import helpers
|
|
22
22
|
from nautobot.core.testing import mixins, utils
|
|
23
23
|
from nautobot.core.utils import lookup
|
|
24
|
-
from nautobot.dcim.models.device_components import ComponentModel
|
|
25
24
|
from nautobot.extras import choices as extras_choices, models as extras_models, querysets as extras_querysets
|
|
26
25
|
from nautobot.extras.forms import CustomFieldModelFormMixin, RelationshipModelFormMixin
|
|
27
26
|
from nautobot.extras.models import CustomFieldModel, RelationshipModel
|
|
28
|
-
from nautobot.extras.models.jobs import JobResult
|
|
29
27
|
from nautobot.extras.models.mixins import NotesMixin
|
|
30
28
|
from nautobot.ipam.models import Prefix
|
|
31
29
|
from nautobot.users import models as users_models
|
|
32
30
|
|
|
33
31
|
__all__ = (
|
|
32
|
+
"TestCase",
|
|
34
33
|
"ModelTestCase",
|
|
35
34
|
"ModelViewTestCase",
|
|
36
|
-
"TestCase",
|
|
37
35
|
"ViewTestCases",
|
|
38
36
|
)
|
|
39
37
|
|
|
@@ -1021,17 +1019,6 @@ class ViewTestCases:
|
|
|
1021
1019
|
for instance in self._get_queryset().filter(pk__in=pk_list):
|
|
1022
1020
|
self.assertInstanceEqual(instance, self.bulk_edit_data)
|
|
1023
1021
|
|
|
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
|
-
|
|
1035
1022
|
def test_bulk_edit_objects_without_permission(self):
|
|
1036
1023
|
pk_list = list(self._get_queryset().values_list("pk", flat=True)[:3])
|
|
1037
1024
|
data = {
|
|
@@ -1057,8 +1044,9 @@ class ViewTestCases:
|
|
|
1057
1044
|
# Assign model-level permission
|
|
1058
1045
|
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1059
1046
|
|
|
1060
|
-
|
|
1061
|
-
self.
|
|
1047
|
+
# Try POST with model-level permission
|
|
1048
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
|
|
1049
|
+
self.validate_object_data_after_bulk_edit(pk_list)
|
|
1062
1050
|
|
|
1063
1051
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1064
1052
|
def test_bulk_edit_form_contains_all_pks(self):
|
|
@@ -1083,7 +1071,7 @@ class ViewTestCases:
|
|
|
1083
1071
|
for pk in pk_list:
|
|
1084
1072
|
self.assertNotIn(str(pk), response_body)
|
|
1085
1073
|
self.assertInHTML(
|
|
1086
|
-
'<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
|
|
1074
|
+
'<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
|
|
1087
1075
|
response_body,
|
|
1088
1076
|
)
|
|
1089
1077
|
|
|
@@ -1119,7 +1107,7 @@ class ViewTestCases:
|
|
|
1119
1107
|
self.assertNotIn(str(third_pk), response_body)
|
|
1120
1108
|
self.assertIn("Editing 2 ", response_body)
|
|
1121
1109
|
self.assertInHTML(
|
|
1122
|
-
'<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
|
|
1110
|
+
'<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
|
|
1123
1111
|
response_body,
|
|
1124
1112
|
)
|
|
1125
1113
|
|
|
@@ -1159,9 +1147,21 @@ class ViewTestCases:
|
|
|
1159
1147
|
|
|
1160
1148
|
# Attempt to bulk edit permitted objects into a non-permitted state
|
|
1161
1149
|
response = self.client.post(self._get_url("bulk_edit"), data)
|
|
1162
|
-
#
|
|
1163
|
-
|
|
1164
|
-
|
|
1150
|
+
# 200 because we're sent back to the edit form to try again; if the update were successful it'd be a 302
|
|
1151
|
+
self.assertHttpStatus(response, 200)
|
|
1152
|
+
# Assert that the objects are NOT updated
|
|
1153
|
+
for instance in self._get_queryset().filter(pk__in=pk_list):
|
|
1154
|
+
self.assertIn(field.value_from_object(instance), values)
|
|
1155
|
+
self.assertNotEqual(field.value_from_object(instance), self.bulk_edit_data[attr_name])
|
|
1156
|
+
|
|
1157
|
+
# Update permission constraints to permit all objects
|
|
1158
|
+
obj_perm.constraints = {"pk__gt": 0}
|
|
1159
|
+
obj_perm.save()
|
|
1160
|
+
|
|
1161
|
+
# Bulk edit permitted objects and expect a redirect back to the list view
|
|
1162
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
|
|
1163
|
+
# Assert that the objects were all updated correctly
|
|
1164
|
+
self.validate_object_data_after_bulk_edit(pk_list)
|
|
1165
1165
|
|
|
1166
1166
|
class BulkDeleteObjectsViewTestCase(ModelViewTestCase):
|
|
1167
1167
|
"""
|
|
@@ -1193,6 +1193,7 @@ class ViewTestCases:
|
|
|
1193
1193
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1194
1194
|
def test_bulk_delete_objects_with_permission(self):
|
|
1195
1195
|
pk_list = self.get_deletable_object_pks()
|
|
1196
|
+
initial_count = self._get_queryset().count()
|
|
1196
1197
|
data = {
|
|
1197
1198
|
"pk": pk_list,
|
|
1198
1199
|
"confirm": True,
|
|
@@ -1200,18 +1201,11 @@ class ViewTestCases:
|
|
|
1200
1201
|
}
|
|
1201
1202
|
|
|
1202
1203
|
# Assign unconstrained permission
|
|
1203
|
-
self.add_permissions(
|
|
1204
|
-
f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}", "extras.view_jobresult"
|
|
1205
|
-
)
|
|
1204
|
+
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
1206
1205
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
self.
|
|
1210
|
-
response,
|
|
1211
|
-
reverse("extras:jobresult", args=[job_result.pk]),
|
|
1212
|
-
status_code=302,
|
|
1213
|
-
target_status_code=200,
|
|
1214
|
-
)
|
|
1206
|
+
# Try POST with model-level permission
|
|
1207
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_delete"), data), 302)
|
|
1208
|
+
self.assertEqual(self._get_queryset().count(), initial_count - len(pk_list))
|
|
1215
1209
|
|
|
1216
1210
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1217
1211
|
def test_bulk_delete_form_contains_all_objects(self):
|
|
@@ -1295,16 +1289,9 @@ class ViewTestCases:
|
|
|
1295
1289
|
obj_perm.constraints = {"pk__isnull": False} # Match a non-existent pk (i.e., allow all)
|
|
1296
1290
|
obj_perm.save()
|
|
1297
1291
|
|
|
1298
|
-
#
|
|
1299
|
-
self.
|
|
1300
|
-
|
|
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
|
-
)
|
|
1292
|
+
# Bulk delete permitted objects
|
|
1293
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_delete"), data), 302)
|
|
1294
|
+
self.assertEqual(self._get_queryset().count(), initial_count - len(pk_list))
|
|
1308
1295
|
|
|
1309
1296
|
class BulkRenameObjectsViewTestCase(ModelViewTestCase):
|
|
1310
1297
|
"""
|
|
@@ -1454,8 +1441,6 @@ class ViewTestCases:
|
|
|
1454
1441
|
maxDiff = None
|
|
1455
1442
|
bulk_add_data = None
|
|
1456
1443
|
"""Used for bulk-add (distinct from bulk-create) view testing; self.bulk_create_data will be used if unset."""
|
|
1457
|
-
selected_objects: list[ComponentModel]
|
|
1458
|
-
selected_objects_parent_name: str
|
|
1459
1444
|
|
|
1460
1445
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1461
1446
|
def test_bulk_add_component(self):
|
|
@@ -9,6 +9,7 @@ from example_app.models import ExampleModel
|
|
|
9
9
|
class AppHomeTestCase(SeleniumTestCase):
|
|
10
10
|
"""Integration test the Example App homepage extensions."""
|
|
11
11
|
|
|
12
|
+
fixtures = ["user-data.json"] # bob/bob
|
|
12
13
|
layout = {
|
|
13
14
|
"Organization": {
|
|
14
15
|
"Locations": {"model": Location, "permission": "dcim.view_location"},
|
|
@@ -11,6 +11,8 @@ from nautobot.extras.models import CustomField, CustomFieldChoice, Status
|
|
|
11
11
|
class ListViewFilterTestCase(SeleniumTestCase):
|
|
12
12
|
"""Integration test for the list view filter ui."""
|
|
13
13
|
|
|
14
|
+
fixtures = ["user-data.json"]
|
|
15
|
+
|
|
14
16
|
def setUp(self):
|
|
15
17
|
super().setUp()
|
|
16
18
|
self.login(self.user.username, self.password)
|
|
@@ -7,6 +7,7 @@ from nautobot.tenancy.models import Tenant
|
|
|
7
7
|
class HomeTestCase(SeleniumTestCase):
|
|
8
8
|
"""Integration tests against the home page."""
|
|
9
9
|
|
|
10
|
+
fixtures = ["user-data.json"] # bob/bob
|
|
10
11
|
layout = {
|
|
11
12
|
"Organization": {
|
|
12
13
|
"Locations": {"model": Location, "permission": "dcim.view_location"},
|
nautobot/core/tests/runner.py
CHANGED
|
@@ -142,7 +142,7 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
142
142
|
# branches/releases of Nautobot in separate files.
|
|
143
143
|
hexdigest = hashlib.shake_128(
|
|
144
144
|
",".join(
|
|
145
|
-
sorted(f"{m.app}.{m.name}" for m in MigrationRecorder.Migration.objects.all())
|
|
145
|
+
sorted(f"{m.app}.{m.name}" for m in MigrationRecorder.Migration.objects.all())
|
|
146
146
|
).encode("utf-8")
|
|
147
147
|
).hexdigest(10)
|
|
148
148
|
command += ["--fixture-file", f"development/factory_dump.{hexdigest}.json"]
|