nautobot 3.0.3__py3-none-any.whl → 3.0.5__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.
- nautobot/core/authentication.py +0 -1
- nautobot/core/celery/schedulers.py +1 -3
- nautobot/core/cli/__init__.py +81 -39
- nautobot/core/settings.yaml +12 -4
- nautobot/core/tables.py +28 -17
- nautobot/core/templates/graphene/graphiql.html +3 -5
- nautobot/core/templates/inc/javascript.html +5 -10
- nautobot/core/templates/inc/media.html +5 -4
- nautobot/core/templates/inc/media_failure.html +73 -0
- nautobot/core/templates/media_failure.html +1 -0
- nautobot/core/tests/test_cli.py +120 -1
- nautobot/core/tests/test_templatetags_helpers.py +9 -9
- nautobot/core/ui/object_detail.py +1 -0
- nautobot/dcim/forms.py +1 -0
- nautobot/dcim/tables/devices.py +6 -5
- nautobot/dcim/tables/template_code.py +8 -4
- nautobot/dcim/templates/dcim/platform_create.html +3 -4
- nautobot/dcim/tests/test_tables.py +5 -6
- nautobot/dcim/views.py +6 -7
- nautobot/extras/models/jobs.py +7 -1
- nautobot/extras/signals.py +143 -113
- nautobot/extras/tables.py +3 -3
- nautobot/extras/templates/extras/inc/jobresult_js.html +1 -2
- nautobot/extras/templates/extras/scheduledjob.html +3 -1
- nautobot/extras/tests/test_customfields.py +75 -9
- nautobot/extras/tests/test_utils.py +116 -1
- nautobot/extras/utils.py +18 -16
- nautobot/extras/views.py +2 -14
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/filters.py +58 -3
- nautobot/ipam/tables.py +8 -4
- nautobot/ipam/tests/test_filters.py +55 -0
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/docs/404.html +64 -8
- nautobot/project-static/docs/apps/index.html +64 -8
- nautobot/project-static/docs/apps/nautobot-apps.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/templatetags.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +64 -8
- nautobot/project-static/docs/development/apps/api/configuration-view.html +64 -8
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +67 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/graphql.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/queryset.html +13440 -0
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +64 -8
- nautobot/project-static/docs/development/apps/api/prometheus.html +64 -8
- nautobot/project-static/docs/development/apps/api/setup.html +64 -8
- nautobot/project-static/docs/development/apps/api/testing.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/base-template.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/index.html +67 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/notes.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/urls.html +64 -8
- nautobot/project-static/docs/development/apps/index.html +64 -8
- nautobot/project-static/docs/development/apps/migration/code-updates.html +64 -8
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v1.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/migrating-v2-to-v3.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/new-nautobot-custom-ui-apis.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/overview.html +68 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/upgrading-from-bootstrap-v3-to-v5.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +64 -8
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +64 -8
- nautobot/project-static/docs/development/core/application-registry.html +64 -8
- nautobot/project-static/docs/development/core/best-practices.html +64 -8
- nautobot/project-static/docs/development/core/caching.html +64 -8
- nautobot/project-static/docs/development/core/controllers.html +64 -8
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +64 -8
- nautobot/project-static/docs/development/core/docs-media-standards.html +64 -8
- nautobot/project-static/docs/development/core/generic-views.html +64 -8
- nautobot/project-static/docs/development/core/getting-started.html +64 -8
- nautobot/project-static/docs/development/core/homepage.html +64 -8
- nautobot/project-static/docs/development/core/index.html +64 -8
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +64 -8
- nautobot/project-static/docs/development/core/model-checklist.html +64 -8
- nautobot/project-static/docs/development/core/model-features.html +64 -8
- nautobot/project-static/docs/development/core/natural-keys.html +64 -8
- nautobot/project-static/docs/development/core/navigation-menu.html +64 -8
- nautobot/project-static/docs/development/core/release-checklist.html +66 -8
- nautobot/project-static/docs/development/core/role-internals.html +64 -8
- nautobot/project-static/docs/development/core/settings.html +64 -8
- nautobot/project-static/docs/development/core/style-guide.html +64 -8
- nautobot/project-static/docs/development/core/templates.html +64 -8
- nautobot/project-static/docs/development/core/testing.html +64 -8
- nautobot/project-static/docs/development/core/ui-best-practices.html +64 -8
- nautobot/project-static/docs/development/core/ui-component-framework.html +64 -8
- nautobot/project-static/docs/development/core/user-preferences.html +64 -8
- nautobot/project-static/docs/development/index.html +64 -8
- nautobot/project-static/docs/development/jobs/getting-started.html +64 -8
- nautobot/project-static/docs/development/jobs/index.html +64 -8
- nautobot/project-static/docs/development/jobs/installation.html +64 -8
- nautobot/project-static/docs/development/jobs/job-extensions.html +64 -8
- nautobot/project-static/docs/development/jobs/job-logging.html +64 -8
- nautobot/project-static/docs/development/jobs/job-patterns.html +64 -8
- nautobot/project-static/docs/development/jobs/job-structure.html +64 -8
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +64 -8
- nautobot/project-static/docs/development/jobs/testing.html +64 -8
- nautobot/project-static/docs/index.html +64 -8
- nautobot/project-static/docs/overview/application_stack.html +64 -8
- nautobot/project-static/docs/overview/design_philosophy.html +64 -8
- nautobot/project-static/docs/release-notes/index.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.0.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.1.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.2.html +65 -9
- nautobot/project-static/docs/release-notes/version-1.3.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.4.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.5.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.6.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.0.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.1.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.2.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.3.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.4.html +584 -8
- nautobot/project-static/docs/release-notes/version-3.0.html +467 -8
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +337 -329
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +75 -12
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +72 -9
- nautobot/project-static/docs/user-guide/administration/installation/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/services.html +64 -8
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +65 -9
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +64 -8
- nautobot/project-static/docs/user-guide/administration/security/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/security/notices.html +64 -8
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +64 -8
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +67 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v2/index.html +68 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/postgresql.html +13391 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +125 -15
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/certificateprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/healthcheckmonitor.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/loadbalancerpool.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/loadbalancerpoolmember.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/virtualserver.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpn.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnphase1policy.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnphase2policy.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpntunnel.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpntunnelendpoint.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/data-compliance.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +75 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +85 -17
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/12-add-tenant-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/12-add-tenant-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/13-assign-tenant-to-device-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/13-assign-tenant-to-device-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/14-assign-tenant-to-device-2-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/14-assign-tenant-to-device-2-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/22-create-vlans-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/22-create-vlans-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/23-create-vlans-2-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/23-create-vlans-2-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/24-vlan-main-page-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/24-vlan-main-page-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/25-add-vlan-to-interface-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/25-add-vlan-to-interface-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/26-add-vlan-to-interface-2-dark.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/26-add-vlan-to-interface-2-light.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/load-balancers.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +64 -8
- nautobot/project-static/docs/user-guide/index.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/approval-workflow.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/data-validation.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/echarts.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +116 -34
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/configurablecolumns.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/savedview.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/search.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/groups.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +64 -8
- nautobot/tenancy/tables.py +1 -1
- nautobot/ui/package-lock.json +36 -36
- nautobot/ui/package.json +3 -3
- nautobot/ui/src/scss/nautobot.scss +2 -1
- nautobot/users/models.py +33 -0
- nautobot/users/tests/test_models.py +83 -0
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/METADATA +4 -4
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/RECORD +397 -386
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/12-add-tenant.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/13-assign-tenant-to-device.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/14-assign-tenant-to-device-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/22-create-vlans.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/23-create-vlans-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/24-vlan-main-page.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/25-add-vlan-to-interface.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/getting-started-nautobot-ui/26-add-vlan-to-interface-2.png +0 -0
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/NOTICE +0 -0
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/WHEEL +0 -0
- {nautobot-3.0.3.dist-info → nautobot-3.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -48,23 +48,23 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
48
48
|
self.assertEqual(
|
|
49
49
|
helpers.hyperlinked_email("admin@example.com"), '<a href="mailto:admin@example.com">admin@example.com</a>'
|
|
50
50
|
)
|
|
51
|
-
self.assertEqual(helpers.hyperlinked_email(None),
|
|
51
|
+
self.assertEqual(helpers.hyperlinked_email(None), helpers.HTML_NONE)
|
|
52
52
|
|
|
53
53
|
def test_hyperlinked_phone_number(self):
|
|
54
54
|
self.assertEqual(helpers.hyperlinked_phone_number("555-1234"), '<a href="tel:555-1234">555-1234</a>')
|
|
55
|
-
self.assertEqual(helpers.hyperlinked_phone_number(None),
|
|
55
|
+
self.assertEqual(helpers.hyperlinked_phone_number(None), helpers.HTML_NONE)
|
|
56
56
|
|
|
57
57
|
def test_placeholder(self):
|
|
58
|
-
self.assertEqual(helpers.placeholder(None),
|
|
59
|
-
self.assertEqual(helpers.placeholder([]),
|
|
58
|
+
self.assertEqual(helpers.placeholder(None), helpers.HTML_NONE)
|
|
59
|
+
self.assertEqual(helpers.placeholder([]), helpers.HTML_NONE)
|
|
60
60
|
self.assertEqual(helpers.placeholder("something"), "something")
|
|
61
61
|
|
|
62
62
|
def test_pre_tag(self):
|
|
63
|
-
self.assertEqual(helpers.pre_tag(None),
|
|
63
|
+
self.assertEqual(helpers.pre_tag(None), helpers.HTML_NONE)
|
|
64
64
|
self.assertEqual(helpers.pre_tag([]), "<pre>[]</pre>")
|
|
65
65
|
self.assertEqual(helpers.pre_tag("something"), "<pre>something</pre>")
|
|
66
|
-
self.assertEqual(helpers.pre_tag("", format_empty_value=False),
|
|
67
|
-
self.assertEqual(helpers.pre_tag([], format_empty_value=False),
|
|
66
|
+
self.assertEqual(helpers.pre_tag("", format_empty_value=False), helpers.HTML_NONE)
|
|
67
|
+
self.assertEqual(helpers.pre_tag([], format_empty_value=False), helpers.HTML_NONE)
|
|
68
68
|
self.assertEqual(helpers.pre_tag("something", format_empty_value=False), "<pre>something</pre>")
|
|
69
69
|
|
|
70
70
|
def test_add_html_id(self):
|
|
@@ -263,7 +263,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
263
263
|
helpers.render_boolean(value),
|
|
264
264
|
'<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>',
|
|
265
265
|
)
|
|
266
|
-
self.assertEqual(helpers.render_boolean(None),
|
|
266
|
+
self.assertEqual(helpers.render_boolean(None), helpers.HTML_NONE)
|
|
267
267
|
|
|
268
268
|
def test_hyperlinked_object_with_color(self):
|
|
269
269
|
vlan_with_role = VLAN.objects.filter(role__isnull=False).first()
|
|
@@ -276,7 +276,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
276
276
|
f'<span class="badge" style="color: {fbcolor}; background-color: #{color}">{display}</span>',
|
|
277
277
|
)
|
|
278
278
|
# Assert when obj is None
|
|
279
|
-
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None),
|
|
279
|
+
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), helpers.HTML_NONE)
|
|
280
280
|
|
|
281
281
|
@tag("example_app")
|
|
282
282
|
@override_settings(BANNER_TOP="¡Hola, mundo!")
|
|
@@ -1917,6 +1917,7 @@ class _ObjectCustomFieldsPanel(GroupedKeyValueTablePanel):
|
|
|
1917
1917
|
|
|
1918
1918
|
def render_value(self, key, value, context: Context):
|
|
1919
1919
|
"""Render a given custom field value appropriately depending on what type of custom field it is."""
|
|
1920
|
+
# TODO: this logic could be unified with CustomFieldColumn.render()?
|
|
1920
1921
|
cf = key
|
|
1921
1922
|
if cf.type == CustomFieldTypeChoices.TYPE_BOOLEAN:
|
|
1922
1923
|
return render_boolean(value)
|
nautobot/dcim/forms.py
CHANGED
|
@@ -4516,6 +4516,7 @@ class CableFilterForm(BootstrapMixin, StatusModelFilterFormMixin, forms.Form):
|
|
|
4516
4516
|
color = forms.CharField(max_length=6, required=False, widget=ColorSelect()) # RGB color code
|
|
4517
4517
|
device = DynamicModelMultipleChoiceField(
|
|
4518
4518
|
queryset=Device.objects.all(),
|
|
4519
|
+
to_field_name="name",
|
|
4519
4520
|
required=False,
|
|
4520
4521
|
label="Device",
|
|
4521
4522
|
query_params={
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -11,7 +11,7 @@ from nautobot.core.tables import (
|
|
|
11
11
|
TagColumn,
|
|
12
12
|
ToggleColumn,
|
|
13
13
|
)
|
|
14
|
-
from nautobot.core.templatetags.helpers import humanize_speed
|
|
14
|
+
from nautobot.core.templatetags.helpers import HTML_NONE, humanize_speed
|
|
15
15
|
from nautobot.dcim.models import (
|
|
16
16
|
ConsolePort,
|
|
17
17
|
ConsoleServerPort,
|
|
@@ -204,7 +204,7 @@ class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
204
204
|
vc_priority = tables.Column(verbose_name="VC Priority")
|
|
205
205
|
device_redundancy_group = tables.Column(linkify=True)
|
|
206
206
|
device_redundancy_group_priority = tables.TemplateColumn(
|
|
207
|
-
template_code="""{% if record.device_redundancy_group %}<span class="badge badge-default">{{ record.device_redundancy_group_priority|default:'None' }}</span>{% else %}
|
|
207
|
+
template_code="""{% if record.device_redundancy_group %}<span class="badge badge-default">{{ record.device_redundancy_group_priority|default:'None' }}</span>{% else %}<span class="text-secondary">—</span>{% endif %}"""
|
|
208
208
|
)
|
|
209
209
|
controller_managed_device_group = tables.Column(linkify=True, verbose_name="Device Group")
|
|
210
210
|
software_version = tables.Column(linkify=True, verbose_name="Software Version")
|
|
@@ -263,7 +263,7 @@ class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
263
263
|
def render_capabilities(self, value):
|
|
264
264
|
"""Render capabilities."""
|
|
265
265
|
if not value:
|
|
266
|
-
return
|
|
266
|
+
return HTML_NONE
|
|
267
267
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
268
268
|
|
|
269
269
|
|
|
@@ -1260,6 +1260,7 @@ class DeviceRedundancyGroupTable(BaseTable):
|
|
|
1260
1260
|
fields = (
|
|
1261
1261
|
"pk",
|
|
1262
1262
|
"name",
|
|
1263
|
+
"description",
|
|
1263
1264
|
"status",
|
|
1264
1265
|
"failover_strategy",
|
|
1265
1266
|
"controller_count",
|
|
@@ -1504,7 +1505,7 @@ class ControllerTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
1504
1505
|
def render_capabilities(self, value):
|
|
1505
1506
|
"""Render capabilities."""
|
|
1506
1507
|
if not value:
|
|
1507
|
-
return
|
|
1508
|
+
return HTML_NONE
|
|
1508
1509
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
1509
1510
|
|
|
1510
1511
|
|
|
@@ -1568,7 +1569,7 @@ class ControllerManagedDeviceGroupTable(BaseTable):
|
|
|
1568
1569
|
def render_capabilities(self, value):
|
|
1569
1570
|
"""Render capabilities."""
|
|
1570
1571
|
if not value:
|
|
1571
|
-
return
|
|
1572
|
+
return HTML_NONE
|
|
1572
1573
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
1573
1574
|
|
|
1574
1575
|
|
|
@@ -4,7 +4,7 @@ CABLETERMINATION = """
|
|
|
4
4
|
<i class="mdi mdi-chevron-right"></i>
|
|
5
5
|
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
|
|
6
6
|
{% else %}
|
|
7
|
-
|
|
7
|
+
<span class="text-secondary">—</span>
|
|
8
8
|
{% endif %}
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -26,12 +26,16 @@ PATHENDPOINT = """
|
|
|
26
26
|
{% endfor %}
|
|
27
27
|
{% endwith %}
|
|
28
28
|
{% else %}
|
|
29
|
-
|
|
29
|
+
<span class="text-secondary">—</span>
|
|
30
30
|
{% endif %}
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
CABLE_LENGTH = """
|
|
34
|
-
{% if record.length %}
|
|
34
|
+
{% if record.length %}
|
|
35
|
+
{{ record.length }} {{ record.get_length_unit_display }}
|
|
36
|
+
{% else %}
|
|
37
|
+
<span class="text-secondary">—</span>
|
|
38
|
+
{% endif %}
|
|
35
39
|
"""
|
|
36
40
|
|
|
37
41
|
CABLE_TERMINATION_PARENT = """
|
|
@@ -83,7 +87,7 @@ INTERFACE_TAGGED_VLANS = """
|
|
|
83
87
|
{% elif record.mode == 'tagged-all' %}
|
|
84
88
|
All
|
|
85
89
|
{% else %}
|
|
86
|
-
|
|
90
|
+
<span class="text-secondary">—</span>
|
|
87
91
|
{% endif %}
|
|
88
92
|
"""
|
|
89
93
|
|
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
{% block form_fields %}
|
|
8
8
|
{% render_field form.name %}
|
|
9
|
-
{% render_field form.slug %}
|
|
10
9
|
{% render_field form.manufacturer %}
|
|
11
10
|
|
|
12
|
-
<div class="mb-10 d-flex justify-content-center{% if form.network_driver.errors %} has-error{% endif %}">
|
|
13
|
-
<label class="col-
|
|
14
|
-
<div class="col-
|
|
11
|
+
<div class="mb-10 d-md-flex justify-content-center{% if form.network_driver.errors %} has-error{% endif %}">
|
|
12
|
+
<label class="col-md-3 col-form-label" for="id_network_driver">Network driver</label>
|
|
13
|
+
<div class="col-md-9">
|
|
15
14
|
{{ form.network_driver }}
|
|
16
15
|
<span class="form-text">
|
|
17
16
|
The <a href="https://netutils.readthedocs.io/en/latest/user/lib_use_cases_lib_mapper/">normalized network driver</a> to use when interacting with devices
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.test import TestCase
|
|
2
2
|
|
|
3
|
+
from nautobot.core.templatetags import helpers
|
|
3
4
|
from nautobot.dcim.choices import InterfaceDuplexChoices, InterfaceSpeedChoices, InterfaceTypeChoices
|
|
4
5
|
from nautobot.dcim.models import Device, DeviceType, Interface, InterfaceTemplate, Location, LocationType, Manufacturer
|
|
5
6
|
from nautobot.dcim.tables.devices import DeviceModuleInterfaceTable, InterfaceTable
|
|
@@ -52,7 +53,6 @@ class InterfaceTableRenderMixin:
|
|
|
52
53
|
|
|
53
54
|
def test_render_speed_duplex_with_none(self):
|
|
54
55
|
"""Test that the table handles None speed value and renders an emdash."""
|
|
55
|
-
emdash = "\u2014"
|
|
56
56
|
interface = Interface.objects.create(
|
|
57
57
|
device=self.device,
|
|
58
58
|
name="eth1",
|
|
@@ -67,8 +67,8 @@ class InterfaceTableRenderMixin:
|
|
|
67
67
|
rendered_speed = bound_row.get_cell("speed")
|
|
68
68
|
rendered_duplex = bound_row.get_cell("duplex")
|
|
69
69
|
|
|
70
|
-
self.assertEqual(rendered_speed,
|
|
71
|
-
self.assertEqual(rendered_duplex,
|
|
70
|
+
self.assertEqual(rendered_speed, helpers.HTML_NONE)
|
|
71
|
+
self.assertEqual(rendered_duplex, helpers.HTML_NONE)
|
|
72
72
|
|
|
73
73
|
def test_render_speed_various(self):
|
|
74
74
|
"""Test that the table correctly humanizes various speed values."""
|
|
@@ -146,7 +146,6 @@ class InterfaceTemplateTableTestCase(TestCase):
|
|
|
146
146
|
self.assertEqual(rendered_duplex, "Full")
|
|
147
147
|
|
|
148
148
|
def test_render_speed_duplex_with_none(self):
|
|
149
|
-
emdash = "\u2014"
|
|
150
149
|
interface_template = InterfaceTemplate.objects.create(
|
|
151
150
|
device_type=self.device_type,
|
|
152
151
|
name="tmpl-eth1",
|
|
@@ -156,5 +155,5 @@ class InterfaceTemplateTableTestCase(TestCase):
|
|
|
156
155
|
bound_row = table.rows[0]
|
|
157
156
|
rendered_speed = bound_row.get_cell("speed") # pylint: disable=no-member
|
|
158
157
|
rendered_duplex = bound_row.get_cell("duplex") # pylint: disable=no-member
|
|
159
|
-
self.assertEqual(rendered_speed,
|
|
160
|
-
self.assertEqual(rendered_duplex,
|
|
158
|
+
self.assertEqual(rendered_speed, helpers.HTML_NONE)
|
|
159
|
+
self.assertEqual(rendered_duplex, helpers.HTML_NONE)
|
nautobot/dcim/views.py
CHANGED
|
@@ -35,7 +35,6 @@ from nautobot.core.exceptions import AbortTransaction
|
|
|
35
35
|
from nautobot.core.forms import BulkRenameForm, ConfirmationForm, ImportForm, restrict_form_fields
|
|
36
36
|
from nautobot.core.models.querysets import count_related
|
|
37
37
|
from nautobot.core.templatetags import helpers
|
|
38
|
-
from nautobot.core.templatetags.helpers import bettertitle, has_perms
|
|
39
38
|
from nautobot.core.ui import object_detail
|
|
40
39
|
from nautobot.core.ui.breadcrumbs import (
|
|
41
40
|
AncestorsInstanceBreadcrumbItem,
|
|
@@ -559,14 +558,14 @@ class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
|
559
558
|
migrate_action = request.POST.get("action")
|
|
560
559
|
try:
|
|
561
560
|
with transaction.atomic():
|
|
562
|
-
if not has_perms(request.user, ["extras.add_contactassociation"]):
|
|
561
|
+
if not helpers.has_perms(request.user, ["extras.add_contactassociation"]):
|
|
563
562
|
raise PermissionDenied(
|
|
564
563
|
"ObjectPermission extras.add_contactassociation is needed to perform this action"
|
|
565
564
|
)
|
|
566
565
|
contact = None
|
|
567
566
|
team = None
|
|
568
567
|
if migrate_action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_CONTACT:
|
|
569
|
-
if not has_perms(request.user, ["extras.add_contact"]):
|
|
568
|
+
if not helpers.has_perms(request.user, ["extras.add_contact"]):
|
|
570
569
|
raise PermissionDenied("ObjectPermission extras.add_contact is needed to perform this action")
|
|
571
570
|
contact = Contact(
|
|
572
571
|
name=request.POST.get("name"),
|
|
@@ -577,7 +576,7 @@ class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
|
577
576
|
# Trigger permission check
|
|
578
577
|
Contact.objects.restrict(request.user, "view").get(pk=contact.pk)
|
|
579
578
|
elif migrate_action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_TEAM:
|
|
580
|
-
if not has_perms(request.user, ["extras.add_team"]):
|
|
579
|
+
if not helpers.has_perms(request.user, ["extras.add_team"]):
|
|
581
580
|
raise PermissionDenied("ObjectPermission extras.add_team is needed to perform this action")
|
|
582
581
|
team = Team(
|
|
583
582
|
name=request.POST.get("name"),
|
|
@@ -958,7 +957,7 @@ class DeviceTypeFieldsPanel(object_detail.ObjectFieldsPanel):
|
|
|
958
957
|
image.url,
|
|
959
958
|
image.name,
|
|
960
959
|
)
|
|
961
|
-
return
|
|
960
|
+
return helpers.HTML_NONE
|
|
962
961
|
|
|
963
962
|
return super().render_value(key, value, context)
|
|
964
963
|
|
|
@@ -2309,7 +2308,7 @@ class DeviceComponentPageMixin:
|
|
|
2309
2308
|
view_name=device_breadcrumb_url,
|
|
2310
2309
|
should_render=lambda c: c["object"].device is not None,
|
|
2311
2310
|
reverse_kwargs=lambda c: {"pk": c["object"].device.pk},
|
|
2312
|
-
label=lambda c: bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2311
|
+
label=lambda c: helpers.bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2313
2312
|
),
|
|
2314
2313
|
)
|
|
2315
2314
|
|
|
@@ -2319,7 +2318,7 @@ class DeviceComponentPageMixin:
|
|
|
2319
2318
|
view_name=module_breadcrumb_url,
|
|
2320
2319
|
should_render=lambda c: c["object"].device is None,
|
|
2321
2320
|
reverse_kwargs=lambda c: {"pk": c["object"].module.pk},
|
|
2322
|
-
label=lambda c: bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2321
|
+
label=lambda c: helpers.bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2323
2322
|
),
|
|
2324
2323
|
)
|
|
2325
2324
|
|
nautobot/extras/models/jobs.py
CHANGED
|
@@ -253,6 +253,7 @@ class Job(PrimaryModel):
|
|
|
253
253
|
)
|
|
254
254
|
objects = BaseManager.from_queryset(JobQuerySet)()
|
|
255
255
|
is_data_compliance_model = False
|
|
256
|
+
is_version_controlled = False
|
|
256
257
|
|
|
257
258
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
258
259
|
|
|
@@ -533,6 +534,7 @@ class JobLogEntry(BaseModel):
|
|
|
533
534
|
|
|
534
535
|
is_metadata_associable_model = False
|
|
535
536
|
is_data_compliance_model = False
|
|
537
|
+
is_version_controlled = False
|
|
536
538
|
|
|
537
539
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
538
540
|
hide_in_diff_view = True
|
|
@@ -585,6 +587,7 @@ class JobQueue(PrimaryModel):
|
|
|
585
587
|
|
|
586
588
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobqueue.html"
|
|
587
589
|
is_data_compliance_model = False
|
|
590
|
+
is_version_controlled = False
|
|
588
591
|
|
|
589
592
|
class Meta:
|
|
590
593
|
ordering = ["name"]
|
|
@@ -616,6 +619,7 @@ class JobQueueAssignment(BaseModel):
|
|
|
616
619
|
job_queue = models.ForeignKey(JobQueue, on_delete=models.CASCADE, related_name="job_assignments")
|
|
617
620
|
is_metadata_associable_model = False
|
|
618
621
|
is_data_compliance_model = False
|
|
622
|
+
is_version_controlled = False
|
|
619
623
|
|
|
620
624
|
class Meta:
|
|
621
625
|
unique_together = ["job", "job_queue"]
|
|
@@ -695,6 +699,7 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
695
699
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
696
700
|
hide_in_diff_view = True
|
|
697
701
|
is_data_compliance_model = False
|
|
702
|
+
is_version_controlled = False
|
|
698
703
|
|
|
699
704
|
def __init__(self, *args, **kwargs):
|
|
700
705
|
super().__init__(*args, **kwargs)
|
|
@@ -897,7 +902,7 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
897
902
|
# so that `run_kubernetes_job_and_return_job_result` is not executed again and the job will be run locally.
|
|
898
903
|
if job_queue.queue_type == JobQueueTypeChoices.TYPE_KUBERNETES and not synchronous:
|
|
899
904
|
# TODO: make this branch aware!
|
|
900
|
-
return run_kubernetes_job_and_return_job_result(
|
|
905
|
+
return run_kubernetes_job_and_return_job_result(job_result, json.dumps(job_kwargs))
|
|
901
906
|
|
|
902
907
|
job_celery_kwargs = {
|
|
903
908
|
"nautobot_job_job_model_id": job_model.id,
|
|
@@ -1277,6 +1282,7 @@ class ScheduledJob(ApprovableModelMixin, BaseModel):
|
|
|
1277
1282
|
|
|
1278
1283
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html"
|
|
1279
1284
|
is_data_compliance_model = False
|
|
1285
|
+
is_version_controlled = False
|
|
1280
1286
|
|
|
1281
1287
|
def __str__(self):
|
|
1282
1288
|
return f"{self.name}: {self.interval}"
|
nautobot/extras/signals.py
CHANGED
|
@@ -8,6 +8,7 @@ import uuid
|
|
|
8
8
|
|
|
9
9
|
from db_file_storage.model_utils import delete_file
|
|
10
10
|
from db_file_storage.storage import DatabaseFileStorage
|
|
11
|
+
from django.conf import settings
|
|
11
12
|
from django.contrib.contenttypes.models import ContentType
|
|
12
13
|
from django.core.cache import cache
|
|
13
14
|
from django.core.exceptions import ValidationError
|
|
@@ -19,6 +20,7 @@ from django.utils import timezone
|
|
|
19
20
|
from django_prometheus.models import model_deletes, model_inserts, model_updates
|
|
20
21
|
import redis.exceptions
|
|
21
22
|
|
|
23
|
+
from nautobot.core.branching import BranchContext
|
|
22
24
|
from nautobot.core.celery import app, import_jobs
|
|
23
25
|
from nautobot.core.models import BaseModel
|
|
24
26
|
from nautobot.core.utils.cache import construct_cache_key
|
|
@@ -214,6 +216,29 @@ def invalidate_gitrepository_provided_contents_cache(sender, **kwargs):
|
|
|
214
216
|
cache.delete_pattern(f"{cache_key}(*)")
|
|
215
217
|
|
|
216
218
|
|
|
219
|
+
def _object_change_branch_name(instance):
|
|
220
|
+
"""
|
|
221
|
+
Get the version-control branch name (if any) that needs to be switched to for ObjectChanges on a given instance.
|
|
222
|
+
"""
|
|
223
|
+
if "nautobot_version_control" not in settings.PLUGINS:
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
# When modifying non-version-controlled models, which only get committed to DOLT_DEFAULT_BRANCH,
|
|
227
|
+
# we need to ensure that the corresponding ObjectChange also is created there, even if we're otherwise working
|
|
228
|
+
# in a non-default branch at the moment. Failing to do so would result in a Dolt error on transaction commit:
|
|
229
|
+
# "Cannot commit changes on more than one branch / database"
|
|
230
|
+
from nautobot_version_control.constants import DOLT_DEFAULT_BRANCH # pylint: disable=import-error
|
|
231
|
+
from nautobot_version_control.utils import ( # pylint: disable=import-error
|
|
232
|
+
active_branch,
|
|
233
|
+
is_version_controlled_model,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if is_version_controlled_model(instance.__class__) or active_branch() == DOLT_DEFAULT_BRANCH:
|
|
237
|
+
return None # no need to switch branches
|
|
238
|
+
|
|
239
|
+
return DOLT_DEFAULT_BRANCH # need to switch temporarily to the default `main` branch for this record
|
|
240
|
+
|
|
241
|
+
|
|
217
242
|
@receiver(post_save)
|
|
218
243
|
@receiver(m2m_changed)
|
|
219
244
|
def _handle_changed_object(sender, instance, raw=False, **kwargs):
|
|
@@ -242,60 +267,63 @@ def _handle_changed_object(sender, instance, raw=False, **kwargs):
|
|
|
242
267
|
|
|
243
268
|
# Record an ObjectChange if applicable
|
|
244
269
|
if hasattr(instance, "to_objectchange"):
|
|
270
|
+
branch_name = _object_change_branch_name(instance)
|
|
245
271
|
user = change_context.get_user(instance)
|
|
246
|
-
# save a copy of this instance's field cache so it can be restored after serialization
|
|
247
|
-
# to prevent unexpected behavior when chaining multiple signal handlers
|
|
248
|
-
original_cache = instance._state.fields_cache.copy()
|
|
249
|
-
|
|
250
|
-
changed_object_type = ContentType.objects.get_for_model(instance)
|
|
251
|
-
changed_object_id = instance.id
|
|
252
272
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
with BranchContext(branch_name=branch_name, user=user, autocommit=False):
|
|
274
|
+
# save a copy of this instance's field cache so it can be restored after serialization
|
|
275
|
+
# to prevent unexpected behavior when chaining multiple signal handlers
|
|
276
|
+
original_cache = instance._state.fields_cache.copy()
|
|
277
|
+
|
|
278
|
+
changed_object_type = ContentType.objects.get_for_model(instance)
|
|
279
|
+
changed_object_id = instance.id
|
|
280
|
+
|
|
281
|
+
# Generate a unique identifier for this change to stash in the change context
|
|
282
|
+
# This is used for deferred change logging and for looking up related changes without querying the database
|
|
283
|
+
unique_object_change_id = None
|
|
284
|
+
if user is not None:
|
|
285
|
+
unique_object_change_id = f"{changed_object_type.pk}__{changed_object_id}__{user.pk}"
|
|
286
|
+
else:
|
|
287
|
+
unique_object_change_id = f"{changed_object_type.pk}__{changed_object_id}"
|
|
288
|
+
|
|
289
|
+
# If a change already exists for this change_id, user, and object, update it instead of creating a new one.
|
|
290
|
+
# If the object was deleted then recreated with the same pk (don't do this), change the action to update.
|
|
291
|
+
if unique_object_change_id in change_context.deferred_object_changes:
|
|
292
|
+
related_changes = ObjectChange.objects.filter(
|
|
293
|
+
changed_object_type=changed_object_type,
|
|
294
|
+
changed_object_id=changed_object_id,
|
|
295
|
+
user=user,
|
|
296
|
+
request_id=change_context.change_id,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Skip the database check when deferring object changes
|
|
300
|
+
if not change_context.defer_object_changes and related_changes.exists():
|
|
301
|
+
objectchange = instance.to_objectchange(action)
|
|
302
|
+
if objectchange is not None:
|
|
303
|
+
most_recent_change = related_changes.order_by("-time").first()
|
|
304
|
+
if most_recent_change.action == ObjectChangeActionChoices.ACTION_DELETE:
|
|
305
|
+
most_recent_change.action = ObjectChangeActionChoices.ACTION_UPDATE
|
|
306
|
+
most_recent_change.object_data = objectchange.object_data
|
|
307
|
+
most_recent_change.object_data_v2 = objectchange.object_data_v2
|
|
308
|
+
most_recent_change.save()
|
|
281
309
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
310
|
+
else:
|
|
311
|
+
change_context.deferred_object_changes[unique_object_change_id] = [
|
|
312
|
+
{"action": action, "instance": instance, "user": user}
|
|
313
|
+
]
|
|
314
|
+
if not change_context.defer_object_changes:
|
|
315
|
+
objectchange = instance.to_objectchange(action)
|
|
316
|
+
if objectchange is not None:
|
|
317
|
+
objectchange.user = user
|
|
318
|
+
objectchange.request_id = change_context.change_id
|
|
319
|
+
objectchange.change_context = change_context.context
|
|
320
|
+
objectchange.change_context_detail = change_context.context_detail[
|
|
321
|
+
:CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL
|
|
322
|
+
]
|
|
323
|
+
objectchange.save()
|
|
324
|
+
|
|
325
|
+
# restore field cache
|
|
326
|
+
instance._state.fields_cache = original_cache
|
|
299
327
|
|
|
300
328
|
# Increment metric counters
|
|
301
329
|
if action == ObjectChangeActionChoices.ACTION_CREATE:
|
|
@@ -326,71 +354,73 @@ def _handle_deleted_object(sender, instance, **kwargs):
|
|
|
326
354
|
|
|
327
355
|
# Record an ObjectChange if applicable
|
|
328
356
|
if hasattr(instance, "to_objectchange"):
|
|
357
|
+
branch_name = _object_change_branch_name(instance)
|
|
329
358
|
user = change_context.get_user(instance)
|
|
330
359
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
changed_object_type = ContentType.objects.get_for_model(instance)
|
|
336
|
-
changed_object_id = instance.id
|
|
337
|
-
|
|
338
|
-
# Generate a unique identifier for this change to stash in the change context
|
|
339
|
-
# This is used for deferred change logging and for looking up related changes without querying the database
|
|
340
|
-
unique_object_change_id = f"{changed_object_type.pk}__{changed_object_id}__{user.pk}"
|
|
341
|
-
save_new_objectchange = True
|
|
342
|
-
|
|
343
|
-
# if a change already exists for this change_id, user, and object, update it instead of creating a new one
|
|
344
|
-
# except in the case that the object was created and deleted in the same change_id
|
|
345
|
-
# we don't want to create a delete change for an object that never existed
|
|
346
|
-
if unique_object_change_id in change_context.deferred_object_changes:
|
|
347
|
-
cached_related_change = change_context.deferred_object_changes[unique_object_change_id][-1]
|
|
348
|
-
if cached_related_change["action"] != ObjectChangeActionChoices.ACTION_CREATE:
|
|
349
|
-
cached_related_change["action"] = ObjectChangeActionChoices.ACTION_DELETE
|
|
350
|
-
save_new_objectchange = False
|
|
351
|
-
|
|
352
|
-
related_changes = ObjectChange.objects.filter(
|
|
353
|
-
changed_object_type=changed_object_type,
|
|
354
|
-
changed_object_id=changed_object_id,
|
|
355
|
-
user=user,
|
|
356
|
-
request_id=change_context.change_id,
|
|
357
|
-
)
|
|
360
|
+
with BranchContext(branch_name=branch_name, user=user, autocommit=False):
|
|
361
|
+
# save a copy of this instance's field cache so it can be restored after serialization
|
|
362
|
+
# to prevent unexpected behavior when chaining multiple signal handlers
|
|
363
|
+
original_cache = instance._state.fields_cache.copy()
|
|
358
364
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
objectchange
|
|
386
|
-
objectchange
|
|
387
|
-
|
|
388
|
-
:
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
365
|
+
changed_object_type = ContentType.objects.get_for_model(instance)
|
|
366
|
+
changed_object_id = instance.id
|
|
367
|
+
|
|
368
|
+
# Generate a unique identifier for this change to stash in the change context
|
|
369
|
+
# This is used for deferred change logging and for looking up related changes without querying the database
|
|
370
|
+
unique_object_change_id = f"{changed_object_type.pk}__{changed_object_id}__{user.pk}"
|
|
371
|
+
save_new_objectchange = True
|
|
372
|
+
|
|
373
|
+
# if a change already exists for this change_id, user, and object, update it instead of creating a new one
|
|
374
|
+
# except in the case that the object was created and deleted in the same change_id
|
|
375
|
+
# we don't want to create a delete change for an object that never existed
|
|
376
|
+
if unique_object_change_id in change_context.deferred_object_changes:
|
|
377
|
+
cached_related_change = change_context.deferred_object_changes[unique_object_change_id][-1]
|
|
378
|
+
if cached_related_change["action"] != ObjectChangeActionChoices.ACTION_CREATE:
|
|
379
|
+
cached_related_change["action"] = ObjectChangeActionChoices.ACTION_DELETE
|
|
380
|
+
save_new_objectchange = False
|
|
381
|
+
|
|
382
|
+
related_changes = ObjectChange.objects.filter(
|
|
383
|
+
changed_object_type=changed_object_type,
|
|
384
|
+
changed_object_id=changed_object_id,
|
|
385
|
+
user=user,
|
|
386
|
+
request_id=change_context.change_id,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
# Skip the database check when deferring object changes
|
|
390
|
+
if not change_context.defer_object_changes and related_changes.exists():
|
|
391
|
+
objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
|
|
392
|
+
if objectchange is not None:
|
|
393
|
+
most_recent_change = related_changes.order_by("-time").first()
|
|
394
|
+
if most_recent_change.action != ObjectChangeActionChoices.ACTION_CREATE:
|
|
395
|
+
most_recent_change.action = ObjectChangeActionChoices.ACTION_DELETE
|
|
396
|
+
most_recent_change.object_data = objectchange.object_data
|
|
397
|
+
most_recent_change.object_data_v2 = objectchange.object_data_v2
|
|
398
|
+
most_recent_change.save()
|
|
399
|
+
save_new_objectchange = False
|
|
400
|
+
|
|
401
|
+
if save_new_objectchange:
|
|
402
|
+
change_context.deferred_object_changes.setdefault(unique_object_change_id, []).append(
|
|
403
|
+
{
|
|
404
|
+
"action": ObjectChangeActionChoices.ACTION_DELETE,
|
|
405
|
+
"instance": instance,
|
|
406
|
+
"user": user,
|
|
407
|
+
"changed_object_id": changed_object_id,
|
|
408
|
+
"changed_object_type": changed_object_type,
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
if not change_context.defer_object_changes:
|
|
412
|
+
objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
|
|
413
|
+
if objectchange is not None:
|
|
414
|
+
objectchange.user = user
|
|
415
|
+
objectchange.request_id = change_context.change_id
|
|
416
|
+
objectchange.change_context = change_context.context
|
|
417
|
+
objectchange.change_context_detail = change_context.context_detail[
|
|
418
|
+
:CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL
|
|
419
|
+
]
|
|
420
|
+
objectchange.save()
|
|
421
|
+
|
|
422
|
+
# restore field cache
|
|
423
|
+
instance._state.fields_cache = original_cache
|
|
394
424
|
|
|
395
425
|
# Increment metric counters
|
|
396
426
|
model_deletes.labels(instance._meta.model_name).inc()
|