nautobot 2.3.10__py3-none-any.whl → 2.3.12__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/utils.py +2 -0
- nautobot/cloud/tables.py +1 -0
- nautobot/core/forms/forms.py +5 -1
- nautobot/core/settings.py +26 -0
- nautobot/core/settings.yaml +31 -0
- nautobot/core/tables.py +88 -22
- nautobot/core/templates/generic/object_bulk_destroy.html +12 -3
- nautobot/core/templates/generic/object_bulk_update.html +4 -2
- nautobot/core/templates/generic/object_create.html +1 -1
- nautobot/core/templates/generic/object_notes.html +1 -1
- nautobot/core/templates/generic/object_retrieve.html +1 -1
- nautobot/core/templates/rest_framework/api.html +3 -0
- nautobot/core/testing/api.py +3 -1
- nautobot/core/testing/integration.py +64 -0
- nautobot/core/testing/views.py +48 -31
- nautobot/core/tests/integration/test_app_navbar.py +3 -3
- nautobot/core/tests/integration/test_navbar.py +1 -1
- nautobot/core/tests/test_csv.py +3 -0
- nautobot/core/tests/test_utils.py +25 -5
- nautobot/core/utils/lookup.py +35 -0
- nautobot/core/views/generic.py +50 -39
- nautobot/core/views/mixins.py +97 -43
- nautobot/core/views/renderers.py +8 -5
- nautobot/dcim/choices.py +6 -0
- nautobot/dcim/tables/devices.py +5 -6
- nautobot/dcim/templates/dcim/controller_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/device/base.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +8 -8
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +2 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +2 -2
- nautobot/dcim/tests/integration/test_create_device.py +86 -0
- nautobot/extras/group_sync.py +42 -0
- nautobot/extras/models/metadata.py +1 -0
- nautobot/extras/models/models.py +1 -1
- nautobot/extras/plugins/__init__.py +2 -1
- nautobot/extras/tables.py +1 -0
- nautobot/extras/templates/extras/inc/job_table.html +1 -1
- nautobot/extras/tests/test_relationships.py +1 -0
- nautobot/extras/tests/test_views.py +0 -2
- nautobot/extras/views.py +1 -2
- nautobot/ipam/factory.py +3 -0
- nautobot/ipam/filters.py +5 -0
- nautobot/ipam/forms.py +17 -0
- nautobot/ipam/models.py +2 -1
- nautobot/ipam/signals.py +2 -2
- nautobot/ipam/tables.py +6 -13
- nautobot/ipam/templates/ipam/ipaddress_assign.html +2 -2
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -1
- nautobot/ipam/tests/test_models.py +113 -1
- nautobot/ipam/tests/test_views.py +39 -5
- nautobot/project-static/docs/404.html +10 -10
- nautobot/project-static/docs/additional-features/caching.html +1 -2
- nautobot/project-static/docs/additional-features/change-logging.html +1 -2
- nautobot/project-static/docs/additional-features/config-contexts.html +1 -2
- nautobot/project-static/docs/additional-features/healthcheck.html +1 -2
- nautobot/project-static/docs/additional-features/jobs.html +1 -2
- nautobot/project-static/docs/additional-features/prometheus-metrics.html +1 -2
- nautobot/project-static/docs/administration/celery-queues.html +1 -2
- nautobot/project-static/docs/administration/nautobot-server.html +1 -2
- nautobot/project-static/docs/administration/nautobot-shell.html +1 -2
- nautobot/project-static/docs/administration/permissions.html +1 -2
- nautobot/project-static/docs/administration/replicating-nautobot.html +1 -2
- nautobot/project-static/docs/apps/index.html +10 -10
- nautobot/project-static/docs/apps/migrating-jobs-from-nautobot-v1.html +1 -2
- nautobot/project-static/docs/apps/nautobot-apps.html +10 -10
- nautobot/project-static/docs/assets/stylesheets/main.6f8fc17f.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.6f8fc17f.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +141 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +185 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +10 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +104 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +14 -14
- nautobot/project-static/docs/configuration/authentication/ldap.html +1 -2
- nautobot/project-static/docs/configuration/authentication/remote.html +1 -2
- nautobot/project-static/docs/configuration/authentication/sso.html +1 -2
- nautobot/project-static/docs/configuration/index.html +1 -2
- nautobot/project-static/docs/configuration/optional-settings.html +1 -2
- nautobot/project-static/docs/configuration/required-settings.html +1 -2
- nautobot/project-static/docs/core-functionality/circuits.html +1 -2
- nautobot/project-static/docs/core-functionality/device-types.html +1 -2
- nautobot/project-static/docs/core-functionality/devices.html +1 -2
- nautobot/project-static/docs/core-functionality/ipam.html +1 -2
- nautobot/project-static/docs/core-functionality/power.html +1 -2
- nautobot/project-static/docs/core-functionality/secrets.html +1 -2
- nautobot/project-static/docs/core-functionality/services.html +1 -2
- nautobot/project-static/docs/core-functionality/sites-and-racks.html +1 -2
- nautobot/project-static/docs/core-functionality/tenancy.html +1 -2
- nautobot/project-static/docs/core-functionality/virtualization.html +1 -2
- nautobot/project-static/docs/core-functionality/vlans.html +1 -2
- nautobot/project-static/docs/development/application-registry.html +1 -2
- nautobot/project-static/docs/development/apps/api/configuration-view.html +10 -10
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +10 -10
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +10 -10
- nautobot/project-static/docs/development/apps/api/models/global-search.html +10 -10
- nautobot/project-static/docs/development/apps/api/models/graphql.html +10 -10
- nautobot/project-static/docs/development/apps/api/models/index.html +10 -10
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +10 -10
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +10 -10
- nautobot/project-static/docs/development/apps/api/prometheus.html +10 -10
- nautobot/project-static/docs/development/apps/api/setup.html +10 -10
- nautobot/project-static/docs/development/apps/api/testing.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +1 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +10 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +1 -2
- nautobot/project-static/docs/development/apps/api/views/base-template.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/index.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/notes.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/urls.html +10 -10
- nautobot/project-static/docs/development/apps/api/views/view-overrides.html +1 -2
- nautobot/project-static/docs/development/apps/index.html +10 -10
- nautobot/project-static/docs/development/apps/migration/code-updates.html +10 -10
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +10 -10
- nautobot/project-static/docs/development/apps/migration/from-v1.html +10 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +10 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +10 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +10 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +10 -10
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +10 -10
- nautobot/project-static/docs/development/best-practices.html +1 -2
- nautobot/project-static/docs/development/core/application-registry.html +10 -10
- nautobot/project-static/docs/development/core/best-practices.html +10 -10
- nautobot/project-static/docs/development/core/bootstrap-ui.html +10 -10
- nautobot/project-static/docs/development/core/caching.html +10 -10
- nautobot/project-static/docs/development/core/controllers.html +10 -10
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +10 -10
- nautobot/project-static/docs/development/core/extending-models.html +1 -2
- nautobot/project-static/docs/development/core/generic-views.html +10 -10
- nautobot/project-static/docs/development/core/getting-started.html +10 -10
- nautobot/project-static/docs/development/core/homepage.html +10 -10
- nautobot/project-static/docs/development/core/index.html +10 -10
- nautobot/project-static/docs/development/core/model-checklist.html +10 -10
- nautobot/project-static/docs/development/core/model-features.html +10 -10
- nautobot/project-static/docs/development/core/natural-keys.html +10 -10
- nautobot/project-static/docs/development/core/navigation-menu.html +10 -10
- nautobot/project-static/docs/development/core/react-ui.html +1 -2
- nautobot/project-static/docs/development/core/release-checklist.html +10 -10
- nautobot/project-static/docs/development/core/role-internals.html +10 -10
- nautobot/project-static/docs/development/core/settings.html +10 -10
- nautobot/project-static/docs/development/core/style-guide.html +10 -10
- nautobot/project-static/docs/development/core/templates.html +10 -10
- nautobot/project-static/docs/development/core/testing.html +12 -12
- nautobot/project-static/docs/development/core/user-preferences.html +10 -10
- nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1 -2
- nautobot/project-static/docs/development/extending-models.html +1 -2
- nautobot/project-static/docs/development/generic-views.html +1 -2
- nautobot/project-static/docs/development/getting-started.html +1 -2
- nautobot/project-static/docs/development/homepage.html +1 -2
- nautobot/project-static/docs/development/index.html +10 -10
- nautobot/project-static/docs/development/jobs/index.html +10 -10
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +10 -10
- nautobot/project-static/docs/development/model-features.html +1 -2
- nautobot/project-static/docs/development/natural-keys.html +1 -2
- nautobot/project-static/docs/development/navigation-menu.html +1 -2
- nautobot/project-static/docs/development/react-ui.html +1 -2
- nautobot/project-static/docs/development/release-checklist.html +1 -2
- nautobot/project-static/docs/development/role-internals.html +1 -2
- nautobot/project-static/docs/development/style-guide.html +1 -2
- nautobot/project-static/docs/development/templates.html +1 -2
- nautobot/project-static/docs/development/testing.html +1 -2
- nautobot/project-static/docs/development/user-preferences.html +1 -2
- nautobot/project-static/docs/docker/index.html +1 -2
- nautobot/project-static/docs/index.html +10 -10
- nautobot/project-static/docs/installation/centos.html +1 -2
- nautobot/project-static/docs/installation/external-authentication.html +1 -2
- nautobot/project-static/docs/installation/http-server.html +1 -2
- nautobot/project-static/docs/installation/index.html +1 -2
- nautobot/project-static/docs/installation/migrating-from-netbox.html +1 -2
- nautobot/project-static/docs/installation/migrating-from-postgresql.html +1 -2
- nautobot/project-static/docs/installation/nautobot.html +1 -2
- nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1 -2
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +1 -2
- nautobot/project-static/docs/installation/services.html +1 -2
- nautobot/project-static/docs/installation/ubuntu.html +1 -2
- nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +1 -2
- nautobot/project-static/docs/installation/upgrading.html +1 -2
- nautobot/project-static/docs/models/circuits/circuit.html +1 -2
- nautobot/project-static/docs/models/circuits/circuittermination.html +1 -2
- nautobot/project-static/docs/models/circuits/circuittype.html +1 -2
- nautobot/project-static/docs/models/circuits/provider.html +1 -2
- nautobot/project-static/docs/models/circuits/providernetwork.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudaccount.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudnetwork.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudresourcetype.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudservice.html +1 -2
- nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +1 -2
- nautobot/project-static/docs/models/dcim/cable.html +1 -2
- nautobot/project-static/docs/models/dcim/consoleport.html +1 -2
- nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/consoleserverport.html +1 -2
- nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/controller.html +1 -2
- nautobot/project-static/docs/models/dcim/controllermanageddevicegroup.html +1 -2
- nautobot/project-static/docs/models/dcim/device.html +1 -2
- nautobot/project-static/docs/models/dcim/devicebay.html +1 -2
- nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/devicefamily.html +1 -2
- nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1 -2
- nautobot/project-static/docs/models/dcim/devicetype.html +1 -2
- nautobot/project-static/docs/models/dcim/frontport.html +1 -2
- nautobot/project-static/docs/models/dcim/frontporttemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/interface.html +1 -2
- nautobot/project-static/docs/models/dcim/interfacetemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/inventoryitem.html +1 -2
- nautobot/project-static/docs/models/dcim/location.html +1 -2
- nautobot/project-static/docs/models/dcim/locationtype.html +1 -2
- nautobot/project-static/docs/models/dcim/manufacturer.html +1 -2
- nautobot/project-static/docs/models/dcim/module.html +1 -2
- nautobot/project-static/docs/models/dcim/modulebay.html +1 -2
- nautobot/project-static/docs/models/dcim/modulebaytemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/moduletype.html +1 -2
- nautobot/project-static/docs/models/dcim/platform.html +1 -2
- nautobot/project-static/docs/models/dcim/powerfeed.html +1 -2
- nautobot/project-static/docs/models/dcim/poweroutlet.html +1 -2
- nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/powerpanel.html +1 -2
- nautobot/project-static/docs/models/dcim/powerport.html +1 -2
- nautobot/project-static/docs/models/dcim/powerporttemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/rack.html +1 -2
- nautobot/project-static/docs/models/dcim/rackgroup.html +1 -2
- nautobot/project-static/docs/models/dcim/rackreservation.html +1 -2
- nautobot/project-static/docs/models/dcim/rearport.html +1 -2
- nautobot/project-static/docs/models/dcim/rearporttemplate.html +1 -2
- nautobot/project-static/docs/models/dcim/softwareimagefile.html +1 -2
- nautobot/project-static/docs/models/dcim/softwareversion.html +1 -2
- nautobot/project-static/docs/models/dcim/virtualchassis.html +1 -2
- nautobot/project-static/docs/models/extras/computedfield.html +1 -2
- nautobot/project-static/docs/models/extras/configcontext.html +1 -2
- nautobot/project-static/docs/models/extras/configcontextschema.html +1 -2
- nautobot/project-static/docs/models/extras/contact.html +1 -2
- nautobot/project-static/docs/models/extras/customfield.html +1 -2
- nautobot/project-static/docs/models/extras/customlink.html +1 -2
- nautobot/project-static/docs/models/extras/dynamicgroup.html +1 -2
- nautobot/project-static/docs/models/extras/exporttemplate.html +1 -2
- nautobot/project-static/docs/models/extras/gitrepository.html +1 -2
- nautobot/project-static/docs/models/extras/jobhook.html +1 -2
- nautobot/project-static/docs/models/extras/joblogentry.html +1 -2
- nautobot/project-static/docs/models/extras/jobresult.html +1 -2
- nautobot/project-static/docs/models/extras/metadatachoice.html +1 -2
- nautobot/project-static/docs/models/extras/metadatatype.html +1 -2
- nautobot/project-static/docs/models/extras/objectmetadata.html +1 -2
- nautobot/project-static/docs/models/extras/role.html +1 -2
- nautobot/project-static/docs/models/extras/savedview.html +1 -2
- nautobot/project-static/docs/models/extras/secret.html +1 -2
- nautobot/project-static/docs/models/extras/secretsgroup.html +1 -2
- nautobot/project-static/docs/models/extras/staticgroupassociation.html +1 -2
- nautobot/project-static/docs/models/extras/status.html +1 -2
- nautobot/project-static/docs/models/extras/team.html +1 -2
- nautobot/project-static/docs/models/ipam/ipaddress.html +1 -2
- nautobot/project-static/docs/models/ipam/prefix.html +1 -2
- nautobot/project-static/docs/models/ipam/rir.html +1 -2
- nautobot/project-static/docs/models/ipam/routetarget.html +1 -2
- nautobot/project-static/docs/models/ipam/service.html +1 -2
- nautobot/project-static/docs/models/ipam/vlan.html +1 -2
- nautobot/project-static/docs/models/ipam/vlangroup.html +1 -2
- nautobot/project-static/docs/models/ipam/vrf.html +1 -2
- nautobot/project-static/docs/models/tenancy/tenant.html +1 -2
- nautobot/project-static/docs/models/tenancy/tenantgroup.html +1 -2
- nautobot/project-static/docs/models/virtualization/cluster.html +1 -2
- nautobot/project-static/docs/models/virtualization/clustergroup.html +1 -2
- nautobot/project-static/docs/models/virtualization/clustertype.html +1 -2
- nautobot/project-static/docs/models/virtualization/virtualmachine.html +1 -2
- nautobot/project-static/docs/models/virtualization/vminterface.html +1 -2
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +10 -10
- nautobot/project-static/docs/overview/design_philosophy.html +10 -10
- nautobot/project-static/docs/overview/index.html +1 -2
- nautobot/project-static/docs/plugins/development.html +1 -2
- nautobot/project-static/docs/plugins/index.html +1 -2
- nautobot/project-static/docs/plugins/porting-from-netbox.html +1 -2
- nautobot/project-static/docs/release-notes/index.html +15 -15
- nautobot/project-static/docs/release-notes/version-1.0.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.1.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.2.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.3.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.4.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.5.html +10 -10
- nautobot/project-static/docs/release-notes/version-1.6.html +10 -10
- nautobot/project-static/docs/release-notes/version-2.0.html +10 -10
- nautobot/project-static/docs/release-notes/version-2.1.html +10 -10
- nautobot/project-static/docs/release-notes/version-2.2.html +10 -10
- nautobot/project-static/docs/release-notes/version-2.3.html +491 -182
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/rest-api/overview.html +1 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +270 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +10 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +10 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +65 -12
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +10 -10
- nautobot/project-static/docs/user-guide/administration/configuration/node-configuration.html +1 -2
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +1 -2
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +10 -10
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +1 -2
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +122 -10
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +1 -2
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +1 -2
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +49 -10
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +10 -10
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +1 -2
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +1 -2
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/index.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +1 -2
- nautobot/project-static/docs/user-guide/administration/installation/services.html +10 -10
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +1 -2
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +1 -2
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +1 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +10 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +10 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +10 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +10 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +10 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +10 -10
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +10 -10
- nautobot/project-static/docs/user-guide/index.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +10 -10
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +10 -10
- nautobot/project-static/docs/user-guides/custom-fields.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/index.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/ipam.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/platforms.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/tenants.html +1 -2
- nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1 -2
- nautobot/project-static/docs/user-guides/git-data-source.html +1 -2
- nautobot/project-static/docs/user-guides/graphql.html +1 -2
- nautobot/project-static/docs/user-guides/ip-address-merge-tool.html +1 -2
- nautobot/project-static/docs/user-guides/relationships.html +1 -2
- nautobot/project-static/docs/user-guides/s3-django-storage.html +1 -2
- nautobot/project-static/js/forms.js +10 -0
- nautobot/virtualization/forms.py +24 -0
- nautobot/virtualization/templates/virtualization/vminterface.html +4 -0
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
- nautobot/virtualization/tests/test_views.py +7 -2
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/METADATA +2 -2
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/RECORD +522 -520
- nautobot/project-static/docs/assets/stylesheets/main.0253249f.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.0253249f.min.css.map +0 -1
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/NOTICE +0 -0
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/WHEEL +0 -0
- {nautobot-2.3.10.dist-info → nautobot-2.3.12.dist-info}/entry_points.txt +0 -0
nautobot/core/testing/views.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import re
|
|
2
3
|
from typing import Optional, Sequence
|
|
3
4
|
from unittest import skipIf
|
|
@@ -792,11 +793,16 @@ class ViewTestCases:
|
|
|
792
793
|
response = self.client.get(f"{self._get_url('list')}?id={instance1.pk}")
|
|
793
794
|
self.assertHttpStatus(response, 200)
|
|
794
795
|
content = utils.extract_page_body(response.content.decode(response.charset))
|
|
796
|
+
# There should be only one row in the table
|
|
797
|
+
self.assertIn('<tr class="even', content)
|
|
798
|
+
self.assertNotIn('<tr class="odd', content)
|
|
795
799
|
if hasattr(self.model, "name"):
|
|
796
800
|
self.assertRegex(content, r">\s*" + re.escape(escape(instance1.name)) + r"\s*<", msg=content)
|
|
797
801
|
self.assertNotRegex(content, r">\s*" + re.escape(escape(instance2.name)) + r"\s*<", msg=content)
|
|
798
|
-
|
|
799
|
-
|
|
802
|
+
with contextlib.suppress(AttributeError):
|
|
803
|
+
# Some models, such as ObjectMetadata, don't have a detail URL
|
|
804
|
+
if instance1.get_absolute_url() in content:
|
|
805
|
+
self.assertNotIn(instance2.get_absolute_url(), content, msg=content)
|
|
800
806
|
|
|
801
807
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"], STRICT_FILTERING=True)
|
|
802
808
|
def test_list_objects_unknown_filter_strict_filtering(self):
|
|
@@ -833,11 +839,16 @@ class ViewTestCases:
|
|
|
833
839
|
content = utils.extract_page_body(response.content.decode(response.charset))
|
|
834
840
|
self.assertNotIn("Unknown filter field", content, msg=content)
|
|
835
841
|
self.assertIn("None", content, msg=content)
|
|
842
|
+
# There should be at least two rows in the table
|
|
843
|
+
self.assertIn('<tr class="even', content)
|
|
844
|
+
self.assertIn('<tr class="odd', content)
|
|
836
845
|
if hasattr(self.model, "name"):
|
|
837
846
|
self.assertRegex(content, r">\s*" + re.escape(escape(instance1.name)) + r"\s*<", msg=content)
|
|
838
847
|
self.assertRegex(content, r">\s*" + re.escape(escape(instance2.name)) + r"\s*<", msg=content)
|
|
839
|
-
|
|
840
|
-
|
|
848
|
+
with contextlib.suppress(AttributeError):
|
|
849
|
+
# Some models, such as ObjectMetadata, don't have a detail URL
|
|
850
|
+
if instance1.get_absolute_url() in content:
|
|
851
|
+
self.assertIn(instance2.get_absolute_url(), content, msg=content)
|
|
841
852
|
|
|
842
853
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
843
854
|
def test_list_objects_without_permission(self):
|
|
@@ -873,6 +884,8 @@ class ViewTestCases:
|
|
|
873
884
|
response_body,
|
|
874
885
|
)
|
|
875
886
|
|
|
887
|
+
return response
|
|
888
|
+
|
|
876
889
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
877
890
|
def test_list_objects_with_constrained_permission(self):
|
|
878
891
|
instance1, instance2 = self._get_queryset().all()[:2]
|
|
@@ -1112,13 +1125,10 @@ class ViewTestCases:
|
|
|
1112
1125
|
|
|
1113
1126
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1114
1127
|
def test_bulk_edit_form_contains_all_pks(self):
|
|
1115
|
-
# We are testing the intermediary step of bulk_edit
|
|
1128
|
+
# We are testing the intermediary step of all bulk_edit.
|
|
1116
1129
|
# i.e. "_all" passed in the form.
|
|
1117
1130
|
pk_list = self._get_queryset().values_list("pk", flat=True)
|
|
1118
|
-
# We only pass in one pk to test the functionality of "_all"
|
|
1119
|
-
# which should grab all instance pks regardless of "pk"
|
|
1120
1131
|
selected_data = {
|
|
1121
|
-
"pk": pk_list[:1],
|
|
1122
1132
|
"_all": "on",
|
|
1123
1133
|
}
|
|
1124
1134
|
# Assign model-level permission
|
|
@@ -1133,13 +1143,19 @@ class ViewTestCases:
|
|
|
1133
1143
|
# after pressing Edit Selected button.
|
|
1134
1144
|
self.assertHttpStatus(response, 200)
|
|
1135
1145
|
response_body = utils.extract_page_body(response.content.decode(response.charset))
|
|
1146
|
+
# Assert the table which shows all the selected objects is not part of the html body in edit all case
|
|
1147
|
+
self.assertNotIn('<table class="table table-hover table-headings">', response_body)
|
|
1136
1148
|
# Check if all the pks are passed into the BulkEditForm/BulkUpdateForm
|
|
1137
1149
|
for pk in pk_list:
|
|
1138
|
-
self.
|
|
1150
|
+
self.assertNotIn(str(pk), response_body)
|
|
1151
|
+
self.assertInHTML(
|
|
1152
|
+
'<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
|
|
1153
|
+
response_body,
|
|
1154
|
+
)
|
|
1139
1155
|
|
|
1140
1156
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1141
1157
|
def test_bulk_edit_form_contains_all_filtered(self):
|
|
1142
|
-
# We are testing the intermediary step of
|
|
1158
|
+
# We are testing the intermediary step of bulk editing all filtered objects.
|
|
1143
1159
|
# i.e. "_all" passed in the form and filter using query params.
|
|
1144
1160
|
self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
|
|
1145
1161
|
|
|
@@ -1155,7 +1171,6 @@ class ViewTestCases:
|
|
|
1155
1171
|
|
|
1156
1172
|
# Open bulk update form with first two objects
|
|
1157
1173
|
selected_data = {
|
|
1158
|
-
"pk": third_pk, # This is ignored when filtering with "_all"
|
|
1159
1174
|
"_all": "on",
|
|
1160
1175
|
**post_data,
|
|
1161
1176
|
}
|
|
@@ -1164,12 +1179,15 @@ class ViewTestCases:
|
|
|
1164
1179
|
# Expect a 200 status cause we are only rendering the bulk edit table after pressing Edit Selected button.
|
|
1165
1180
|
self.assertHttpStatus(response, 200)
|
|
1166
1181
|
response_body = utils.extract_page_body(response.content.decode(response.charset))
|
|
1167
|
-
# Check if
|
|
1168
|
-
self.
|
|
1169
|
-
self.
|
|
1182
|
+
# Check if all pks is not part of the html.
|
|
1183
|
+
self.assertNotIn(str(first_pk), response_body)
|
|
1184
|
+
self.assertNotIn(str(second_pk), response_body)
|
|
1185
|
+
self.assertNotIn(str(third_pk), response_body)
|
|
1170
1186
|
self.assertIn("Editing 2 ", response_body)
|
|
1171
|
-
|
|
1172
|
-
|
|
1187
|
+
self.assertInHTML(
|
|
1188
|
+
'<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
|
|
1189
|
+
response_body,
|
|
1190
|
+
)
|
|
1173
1191
|
|
|
1174
1192
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1175
1193
|
def test_bulk_edit_objects_with_constrained_permission(self):
|
|
@@ -1271,14 +1289,10 @@ class ViewTestCases:
|
|
|
1271
1289
|
self.assertEqual(self._get_queryset().count(), initial_count - len(pk_list))
|
|
1272
1290
|
|
|
1273
1291
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1274
|
-
def
|
|
1275
|
-
# We are testing the intermediary step of bulk_delete
|
|
1292
|
+
def test_bulk_delete_form_contains_all_objects(self):
|
|
1293
|
+
# We are testing the intermediary step of bulk_delete all objects.
|
|
1276
1294
|
# i.e. "_all" passed in the form.
|
|
1277
|
-
pk_list = self._get_queryset().values_list("pk", flat=True)
|
|
1278
|
-
# We only pass in one pk to test the functionality of "_all"
|
|
1279
|
-
# which should grab all instance pks regardless of "pks".
|
|
1280
1295
|
selected_data = {
|
|
1281
|
-
"pk": pk_list[:1],
|
|
1282
1296
|
"confirm": True,
|
|
1283
1297
|
"_all": "on",
|
|
1284
1298
|
}
|
|
@@ -1293,13 +1307,16 @@ class ViewTestCases:
|
|
|
1293
1307
|
response = self.client.post(self._get_url("bulk_delete"), selected_data)
|
|
1294
1308
|
self.assertHttpStatus(response, 200)
|
|
1295
1309
|
response_body = utils.extract_page_body(response.content.decode(response.charset))
|
|
1296
|
-
#
|
|
1297
|
-
|
|
1298
|
-
|
|
1310
|
+
# Assert the table which shows all the selected objects is not part of the html body in delete all case
|
|
1311
|
+
self.assertNotIn('<table class="table table-hover table-headings">', response_body)
|
|
1312
|
+
# Assert none of the hidden input fields for each of the pks that would be deleted is part of the html body
|
|
1313
|
+
for pk in self._get_queryset().values_list("pk", flat=True):
|
|
1314
|
+
self.assertNotIn(str(pk), response_body)
|
|
1315
|
+
self.assertInHTML('<input type="hidden" name="_all" value="true" />', response_body)
|
|
1299
1316
|
|
|
1300
1317
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1301
1318
|
def test_bulk_delete_form_contains_all_filtered(self):
|
|
1302
|
-
# We are testing the intermediary step of bulk_delete with
|
|
1319
|
+
# We are testing the intermediary step of bulk_delete all with additional filter.
|
|
1303
1320
|
# i.e. "_all" passed in the form and filter using query params.
|
|
1304
1321
|
self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
|
|
1305
1322
|
|
|
@@ -1321,12 +1338,12 @@ class ViewTestCases:
|
|
|
1321
1338
|
# Expect a 200 status cause we are only rendering the bulk delete table after pressing Delete Selected button.
|
|
1322
1339
|
self.assertHttpStatus(response, 200)
|
|
1323
1340
|
response_body = utils.extract_page_body(response.content.decode(response.charset))
|
|
1324
|
-
# Check if
|
|
1325
|
-
self.
|
|
1326
|
-
self.
|
|
1341
|
+
# Check if all pks is not part of the html.
|
|
1342
|
+
self.assertNotIn(str(first_pk), response_body)
|
|
1343
|
+
self.assertNotIn(str(second_pk), response_body)
|
|
1344
|
+
self.assertNotIn(str(third_pk), response_body)
|
|
1327
1345
|
self.assertIn("<strong>Warning:</strong> The following operation will delete 2 ", response_body)
|
|
1328
|
-
|
|
1329
|
-
self.assertNotIn(f'<input type="hidden" name="pk" value="{third_pk}"', response_body)
|
|
1346
|
+
self.assertInHTML('<input type="hidden" name="_all" value="true" />', response_body)
|
|
1330
1347
|
|
|
1331
1348
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
1332
1349
|
def test_bulk_delete_objects_with_constrained_permission(self):
|
|
@@ -61,7 +61,7 @@ class AppNavBarTestCase(SeleniumTestCase):
|
|
|
61
61
|
tab_xpath = "//*[@id='navbar']//span[normalize-space()='Example Menu']/.."
|
|
62
62
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
63
63
|
tab.click()
|
|
64
|
-
self.
|
|
64
|
+
self.assertEqual(tab["aria-expanded"], "true")
|
|
65
65
|
|
|
66
66
|
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='Example Group 1']")
|
|
67
67
|
|
|
@@ -82,7 +82,7 @@ class AppNavBarTestCase(SeleniumTestCase):
|
|
|
82
82
|
tab_xpath = "//*[@id='navbar']//*[normalize-space()='Circuits']"
|
|
83
83
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
84
84
|
tab.click()
|
|
85
|
-
self.
|
|
85
|
+
self.assertEqual(tab["aria-expanded"], "true")
|
|
86
86
|
|
|
87
87
|
for group_name, items in self.navbar["Circuits"].items():
|
|
88
88
|
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='{group_name}']")
|
|
@@ -114,7 +114,7 @@ class AppNavBarTestCase(SeleniumTestCase):
|
|
|
114
114
|
tab_xpath = "//*[@id='navbar']//*[normalize-space()='Apps']"
|
|
115
115
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
116
116
|
tab.click()
|
|
117
|
-
self.
|
|
117
|
+
self.assertEqual(tab["aria-expanded"], "true")
|
|
118
118
|
|
|
119
119
|
for group_name, items in self.navbar["Apps"].items():
|
|
120
120
|
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='{group_name}']")
|
|
@@ -60,7 +60,7 @@ class NavBarTestCase(SeleniumTestCase):
|
|
|
60
60
|
tab_xpath = f"//*[@id='navbar']//span[normalize-space()='{tab_name}']/.."
|
|
61
61
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
62
62
|
tab.click()
|
|
63
|
-
self.
|
|
63
|
+
self.assertEqual(tab["aria-expanded"], "true")
|
|
64
64
|
|
|
65
65
|
for group_name, items in groups.items():
|
|
66
66
|
# Append onto tab xpath with group name search
|
nautobot/core/tests/test_csv.py
CHANGED
|
@@ -267,6 +267,9 @@ class CSVParsingRelatedTestCase(TestCase):
|
|
|
267
267
|
url = reverse("dcim:device_import")
|
|
268
268
|
response = self.client.post(url, data)
|
|
269
269
|
self.assertEqual(response.status_code, 200)
|
|
270
|
+
# uploading the CSV always returns a 200 code with a page with an error message on it
|
|
271
|
+
# ensure we don't have that error message
|
|
272
|
+
self.assertNotIn("FORM-ERROR", response.content.decode(response.charset))
|
|
270
273
|
self.assertEqual(Device.objects.count(), 4)
|
|
271
274
|
|
|
272
275
|
# Assert TestDevice3 got created with the right fields
|
|
@@ -21,6 +21,7 @@ from nautobot.extras import models as extras_models, utils as extras_utils
|
|
|
21
21
|
from nautobot.extras.choices import ObjectChangeActionChoices, RelationshipTypeChoices
|
|
22
22
|
from nautobot.extras.models import ObjectChange
|
|
23
23
|
from nautobot.extras.registry import registry
|
|
24
|
+
from nautobot.ipam import models as ipam_models
|
|
24
25
|
|
|
25
26
|
from example_app.models import ExampleModel
|
|
26
27
|
|
|
@@ -164,7 +165,7 @@ class GetFooForModelTest(TestCase):
|
|
|
164
165
|
|
|
165
166
|
def test_get_filterset_for_model(self):
|
|
166
167
|
"""
|
|
167
|
-
Test
|
|
168
|
+
Test that `get_filterset_for_model` returns the right FilterSet for various inputs.
|
|
168
169
|
"""
|
|
169
170
|
self.assertEqual(lookup.get_filterset_for_model("dcim.device"), dcim_filters.DeviceFilterSet)
|
|
170
171
|
self.assertEqual(lookup.get_filterset_for_model(dcim_models.Device), dcim_filters.DeviceFilterSet)
|
|
@@ -173,7 +174,7 @@ class GetFooForModelTest(TestCase):
|
|
|
173
174
|
|
|
174
175
|
def test_get_form_for_model(self):
|
|
175
176
|
"""
|
|
176
|
-
Test
|
|
177
|
+
Test that `get_form_for_model` returns the right Form for various inputs.
|
|
177
178
|
"""
|
|
178
179
|
self.assertEqual(lookup.get_form_for_model("dcim.device", "Filter"), dcim_forms.DeviceFilterForm)
|
|
179
180
|
self.assertEqual(lookup.get_form_for_model(dcim_models.Device, "Filter"), dcim_forms.DeviceFilterForm)
|
|
@@ -184,9 +185,28 @@ class GetFooForModelTest(TestCase):
|
|
|
184
185
|
self.assertEqual(lookup.get_form_for_model("dcim.location"), dcim_forms.LocationForm)
|
|
185
186
|
self.assertEqual(lookup.get_form_for_model(dcim_models.Location), dcim_forms.LocationForm)
|
|
186
187
|
|
|
188
|
+
def test_get_related_field_for_models(self):
|
|
189
|
+
"""
|
|
190
|
+
Test that `get_related_field_for_models` returns the appropriate field for various inputs.
|
|
191
|
+
"""
|
|
192
|
+
# No direct relation found
|
|
193
|
+
self.assertIsNone(lookup.get_related_field_for_models(dcim_models.Device, dcim_models.LocationType))
|
|
194
|
+
# ForeignKey and reverse
|
|
195
|
+
self.assertEqual(lookup.get_related_field_for_models(dcim_models.Device, dcim_models.Location).name, "location")
|
|
196
|
+
self.assertEqual(lookup.get_related_field_for_models(dcim_models.Location, dcim_models.Device).name, "devices")
|
|
197
|
+
# ManyToMany and reverse
|
|
198
|
+
self.assertEqual(
|
|
199
|
+
lookup.get_related_field_for_models(ipam_models.Prefix, dcim_models.Location).name, "locations"
|
|
200
|
+
)
|
|
201
|
+
self.assertEqual(lookup.get_related_field_for_models(dcim_models.Location, ipam_models.Prefix).name, "prefixes")
|
|
202
|
+
# Multiple candidate fields
|
|
203
|
+
with self.assertRaises(AttributeError):
|
|
204
|
+
# both primary_ip4 and primary_ip6 are candidates
|
|
205
|
+
lookup.get_related_field_for_models(dcim_models.Device, ipam_models.IPAddress)
|
|
206
|
+
|
|
187
207
|
def test_get_route_for_model(self):
|
|
188
208
|
"""
|
|
189
|
-
Test
|
|
209
|
+
Test that `get_route_for_model` returns the appropriate URL route name for various inputs.
|
|
190
210
|
"""
|
|
191
211
|
# UI
|
|
192
212
|
self.assertEqual(lookup.get_route_for_model("dcim.device", "list"), "dcim:device_list")
|
|
@@ -221,7 +241,7 @@ class GetFooForModelTest(TestCase):
|
|
|
221
241
|
|
|
222
242
|
def test_get_table_for_model(self):
|
|
223
243
|
"""
|
|
224
|
-
Test
|
|
244
|
+
Test that `get_table_for_model` returns the appropriate Table for various inputs.
|
|
225
245
|
"""
|
|
226
246
|
self.assertEqual(lookup.get_table_for_model("dcim.device"), tables.DeviceTable)
|
|
227
247
|
self.assertEqual(lookup.get_table_for_model(dcim_models.Device), tables.DeviceTable)
|
|
@@ -237,7 +257,7 @@ class GetFooForModelTest(TestCase):
|
|
|
237
257
|
|
|
238
258
|
def test_get_model_for_view_name(self):
|
|
239
259
|
"""
|
|
240
|
-
Test
|
|
260
|
+
Test that `get_model_for_view_name` returns the appropriate Model, if the colon separated view name provided.
|
|
241
261
|
"""
|
|
242
262
|
with self.subTest("Test core view."):
|
|
243
263
|
self.assertEqual(lookup.get_model_for_view_name("dcim:device_list"), dcim_models.Device)
|
nautobot/core/utils/lookup.py
CHANGED
|
@@ -177,6 +177,41 @@ def get_form_for_model(model, form_prefix=""):
|
|
|
177
177
|
return get_related_class_for_model(model, module_name="forms", object_suffix=object_suffix)
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
def get_related_field_for_models(from_model, to_model):
|
|
181
|
+
"""
|
|
182
|
+
Find the field on `from_model` that is a relation to `to_model`.
|
|
183
|
+
|
|
184
|
+
If no such field is found, returns None.
|
|
185
|
+
If more than one such field is found, raises an AttributeError.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
from_model (BaseModel): The model class that should contain the relevant field or relation.
|
|
189
|
+
to_model (BaseModel): The model class that we're looking for as the destination.
|
|
190
|
+
|
|
191
|
+
Examples:
|
|
192
|
+
>>> get_related_field_for_models(Device, Location)
|
|
193
|
+
<django.db.models.fields.related.ForeignKey: location>
|
|
194
|
+
>>> get_related_field_for_models(Location, Device)
|
|
195
|
+
<ManyToOneRel: dcim.device>
|
|
196
|
+
>>> get_related_field_for_models(Prefix, Location)
|
|
197
|
+
<django.db.models.fields.related.ManyToManyField: locations>
|
|
198
|
+
>>> get_related_field_for_models(Location, Prefix)
|
|
199
|
+
<ManyToManyRel: ipam.prefix>
|
|
200
|
+
>>> get_related_field_for_models(Device, IPAddress)
|
|
201
|
+
AttributeError: Device has more than one relation to IPAddress: primary_ip4, primary_ip6
|
|
202
|
+
"""
|
|
203
|
+
matching_field = None
|
|
204
|
+
for field in from_model._meta.get_fields():
|
|
205
|
+
if hasattr(field, "remote_field") and field.remote_field and field.remote_field.model == to_model:
|
|
206
|
+
if matching_field is not None:
|
|
207
|
+
raise AttributeError(
|
|
208
|
+
f"{from_model.__name__} has more than one relation to {to_model.__name__}: "
|
|
209
|
+
f"{matching_field.name}, {field.name}"
|
|
210
|
+
)
|
|
211
|
+
matching_field = field
|
|
212
|
+
return matching_field
|
|
213
|
+
|
|
214
|
+
|
|
180
215
|
def get_table_for_model(model):
|
|
181
216
|
"""Return the `Table` class associated with a given `model`.
|
|
182
217
|
|
nautobot/core/views/generic.py
CHANGED
|
@@ -46,7 +46,7 @@ from nautobot.core.utils.requests import (
|
|
|
46
46
|
get_filterable_params_from_filter_params,
|
|
47
47
|
normalize_querydict,
|
|
48
48
|
)
|
|
49
|
-
from nautobot.core.views.mixins import GetReturnURLMixin, ObjectPermissionRequiredMixin
|
|
49
|
+
from nautobot.core.views.mixins import EditAndDeleteAllModelMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin
|
|
50
50
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
51
51
|
from nautobot.core.views.utils import (
|
|
52
52
|
check_filter_for_display,
|
|
@@ -979,7 +979,7 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): #
|
|
|
979
979
|
)
|
|
980
980
|
|
|
981
981
|
|
|
982
|
-
class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
982
|
+
class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDeleteAllModelMixin, View):
|
|
983
983
|
"""
|
|
984
984
|
Edit objects in bulk.
|
|
985
985
|
|
|
@@ -1013,18 +1013,18 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1013
1013
|
def post(self, request, **kwargs):
|
|
1014
1014
|
logger = logging.getLogger(__name__ + ".BulkEditView")
|
|
1015
1015
|
model = self.queryset.model
|
|
1016
|
+
edit_all = request.POST.get("_all")
|
|
1016
1017
|
|
|
1017
1018
|
# If we are editing *all* objects in the queryset, replace the PK list with all matched objects.
|
|
1018
|
-
if
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
else:
|
|
1022
|
-
pk_list = list(model.objects.all().values_list("pk", flat=True))
|
|
1019
|
+
if edit_all:
|
|
1020
|
+
pk_list = []
|
|
1021
|
+
queryset = self._get_bulk_edit_delete_all_queryset(request)
|
|
1023
1022
|
else:
|
|
1024
1023
|
pk_list = request.POST.getlist("pk")
|
|
1024
|
+
queryset = self.queryset.filter(pk__in=pk_list)
|
|
1025
1025
|
|
|
1026
1026
|
if "_apply" in request.POST:
|
|
1027
|
-
form = self.form(model, request.POST)
|
|
1027
|
+
form = self.form(model, request.POST, edit_all=edit_all)
|
|
1028
1028
|
restrict_form_fields(form, request.user)
|
|
1029
1029
|
|
|
1030
1030
|
if form.is_valid():
|
|
@@ -1041,7 +1041,8 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1041
1041
|
try:
|
|
1042
1042
|
with deferred_change_logging_for_bulk_operation():
|
|
1043
1043
|
updated_objects = []
|
|
1044
|
-
|
|
1044
|
+
queryset = queryset if edit_all else queryset.filter(pk__in=form.cleaned_data["pk"])
|
|
1045
|
+
for obj in queryset:
|
|
1045
1046
|
obj = self.alter_obj(obj, request, [], kwargs)
|
|
1046
1047
|
|
|
1047
1048
|
# Update standard fields. If a field is listed in _nullify, delete its value.
|
|
@@ -1130,23 +1131,26 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1130
1131
|
elif "device_type" in request.GET:
|
|
1131
1132
|
initial_data["device_type"] = request.GET.get("device_type")
|
|
1132
1133
|
|
|
1133
|
-
form = self.form(model, initial=initial_data)
|
|
1134
|
+
form = self.form(model, initial=initial_data, edit_all=edit_all)
|
|
1134
1135
|
restrict_form_fields(form, request.user)
|
|
1135
1136
|
|
|
1136
1137
|
# Retrieve objects being edited
|
|
1137
|
-
table =
|
|
1138
|
-
if not
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1138
|
+
table = None
|
|
1139
|
+
if not edit_all:
|
|
1140
|
+
table = self.table(queryset, orderable=False)
|
|
1141
|
+
if not table.rows:
|
|
1142
|
+
messages.warning(request, f"No {model._meta.verbose_name_plural} were selected.")
|
|
1143
|
+
return redirect(self.get_return_url(request))
|
|
1144
|
+
# Hide actions column if present
|
|
1145
|
+
if "actions" in table.columns:
|
|
1146
|
+
table.columns.hide("actions")
|
|
1144
1147
|
|
|
1145
1148
|
context = {
|
|
1146
1149
|
"form": form,
|
|
1147
1150
|
"table": table,
|
|
1148
1151
|
"obj_type_plural": model._meta.verbose_name_plural,
|
|
1149
1152
|
"return_url": self.get_return_url(request),
|
|
1153
|
+
"objs_count": queryset.count(),
|
|
1150
1154
|
}
|
|
1151
1155
|
context.update(self.extra_context())
|
|
1152
1156
|
return render(request, self.template_name, context)
|
|
@@ -1255,7 +1259,7 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1255
1259
|
return ""
|
|
1256
1260
|
|
|
1257
1261
|
|
|
1258
|
-
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
1262
|
+
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDeleteAllModelMixin, View):
|
|
1259
1263
|
"""
|
|
1260
1264
|
Delete objects in bulk.
|
|
1261
1265
|
|
|
@@ -1278,18 +1282,37 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1278
1282
|
def get(self, request):
|
|
1279
1283
|
return redirect(self.get_return_url(request))
|
|
1280
1284
|
|
|
1281
|
-
def
|
|
1285
|
+
def _perform_delete_operation(self, request, queryset, model):
|
|
1282
1286
|
logger = logging.getLogger(__name__ + ".BulkDeleteView")
|
|
1287
|
+
self.perform_pre_delete(request, queryset)
|
|
1288
|
+
try:
|
|
1289
|
+
_, deleted_info = bulk_delete_with_bulk_change_logging(queryset)
|
|
1290
|
+
deleted_count = deleted_info[model._meta.label]
|
|
1291
|
+
except ProtectedError as e:
|
|
1292
|
+
logger.info("Caught ProtectedError while attempting to delete objects")
|
|
1293
|
+
handle_protectederror(queryset, request, e)
|
|
1294
|
+
return redirect(self.get_return_url(request))
|
|
1295
|
+
msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
|
|
1296
|
+
logger.info(msg)
|
|
1297
|
+
messages.success(request, msg)
|
|
1298
|
+
return redirect(self.get_return_url(request))
|
|
1299
|
+
|
|
1300
|
+
def post(self, request, **kwargs):
|
|
1301
|
+
logger = logging.getLogger(f"{__name__}.BulkDeleteView")
|
|
1283
1302
|
model = self.queryset.model
|
|
1284
1303
|
|
|
1285
1304
|
# Are we deleting *all* objects in the queryset or just a selected subset?
|
|
1286
1305
|
if request.POST.get("_all"):
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1306
|
+
queryset = self._get_bulk_edit_delete_all_queryset(request)
|
|
1307
|
+
|
|
1308
|
+
if "_confirm" in request.POST:
|
|
1309
|
+
return self._perform_delete_operation(request, queryset, model)
|
|
1310
|
+
|
|
1311
|
+
context = self._bulk_delete_all_context(request, queryset)
|
|
1312
|
+
context.update(self.extra_context())
|
|
1313
|
+
return render(request, self.template_name, context)
|
|
1314
|
+
|
|
1315
|
+
pk_list = request.POST.getlist("pk")
|
|
1293
1316
|
|
|
1294
1317
|
form_cls = self.get_form()
|
|
1295
1318
|
|
|
@@ -1300,20 +1323,7 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1300
1323
|
|
|
1301
1324
|
# Delete objects
|
|
1302
1325
|
queryset = self.queryset.filter(pk__in=pk_list)
|
|
1303
|
-
|
|
1304
|
-
self.perform_pre_delete(request, queryset)
|
|
1305
|
-
try:
|
|
1306
|
-
_, deleted_info = bulk_delete_with_bulk_change_logging(queryset)
|
|
1307
|
-
deleted_count = deleted_info[model._meta.label]
|
|
1308
|
-
except ProtectedError as e:
|
|
1309
|
-
logger.info("Caught ProtectedError while attempting to delete objects")
|
|
1310
|
-
handle_protectederror(queryset, request, e)
|
|
1311
|
-
return redirect(self.get_return_url(request))
|
|
1312
|
-
msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
|
|
1313
|
-
logger.info(msg)
|
|
1314
|
-
messages.success(request, msg)
|
|
1315
|
-
return redirect(self.get_return_url(request))
|
|
1316
|
-
|
|
1326
|
+
return self._perform_delete_operation(request, queryset, model)
|
|
1317
1327
|
else:
|
|
1318
1328
|
logger.debug("Form validation failed")
|
|
1319
1329
|
|
|
@@ -1342,6 +1352,7 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1342
1352
|
"obj_type_plural": model._meta.verbose_name_plural,
|
|
1343
1353
|
"table": table,
|
|
1344
1354
|
"return_url": self.get_return_url(request),
|
|
1355
|
+
"total_objs_to_delete": len(table.rows),
|
|
1345
1356
|
}
|
|
1346
1357
|
context.update(self.extra_context())
|
|
1347
1358
|
return render(request, self.template_name, context)
|