nautobot 2.4.0b1__py3-none-any.whl → 2.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/__init__.py +1 -1
- nautobot/apps/api.py +8 -8
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/events.py +3 -3
- nautobot/apps/factory.py +2 -2
- nautobot/apps/filters.py +1 -1
- nautobot/apps/forms.py +20 -20
- nautobot/apps/graphql.py +2 -2
- nautobot/apps/jobs.py +8 -8
- nautobot/apps/models.py +19 -19
- nautobot/apps/tables.py +1 -1
- nautobot/apps/testing.py +10 -10
- nautobot/apps/ui.py +2 -2
- nautobot/apps/utils.py +7 -7
- nautobot/apps/views.py +7 -7
- nautobot/circuits/api/serializers.py +1 -0
- nautobot/circuits/api/views.py +4 -8
- nautobot/circuits/tables.py +2 -1
- nautobot/circuits/templates/circuits/circuit_create.html +1 -7
- nautobot/circuits/views.py +3 -3
- nautobot/cloud/api/views.py +6 -10
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/views.py +0 -16
- nautobot/core/api/constants.py +11 -0
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/filter_backends.py +3 -9
- nautobot/core/api/schema.py +13 -2
- nautobot/core/api/serializers.py +40 -34
- nautobot/core/api/views.py +56 -4
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +2 -2
- nautobot/core/choices.py +2 -2
- nautobot/core/events/__init__.py +3 -3
- nautobot/core/filters.py +67 -35
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +14 -11
- nautobot/core/forms/forms.py +33 -2
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +28 -7
- nautobot/core/jobs/bulk_actions.py +285 -0
- nautobot/core/jobs/cleanup.py +48 -12
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/models/__init__.py +3 -1
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +6 -3
- nautobot/core/settings.py +29 -2
- nautobot/core/settings.yaml +21 -0
- nautobot/core/tables.py +79 -61
- nautobot/core/templates/about.html +67 -0
- nautobot/core/templates/inc/media.html +3 -0
- nautobot/core/templates/inc/nav_menu.html +1 -0
- nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
- nautobot/core/templates/inc/tenant_table_row.html +11 -0
- nautobot/core/templates/nautobot_config.py.j2 +13 -0
- nautobot/core/templates/search.html +7 -0
- nautobot/core/templates/utilities/render_jinja2.html +1 -1
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +7 -0
- nautobot/core/templatetags/helpers.py +11 -2
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +170 -15
- nautobot/core/testing/filters.py +45 -10
- nautobot/core/testing/forms.py +2 -0
- nautobot/core/testing/integration.py +86 -4
- nautobot/core/testing/mixins.py +7 -2
- nautobot/core/testing/views.py +44 -29
- nautobot/core/tests/integration/test_app_home.py +0 -1
- nautobot/core/tests/integration/test_app_navbar.py +0 -1
- nautobot/core/tests/integration/test_filters.py +0 -2
- nautobot/core/tests/integration/test_home.py +0 -1
- nautobot/core/tests/integration/test_navbar.py +0 -1
- nautobot/core/tests/integration/test_view_authentication.py +1 -0
- nautobot/core/tests/runner.py +1 -1
- nautobot/core/tests/test_api.py +98 -1
- nautobot/core/tests/test_csv.py +25 -3
- nautobot/core/tests/test_filters.py +209 -246
- nautobot/core/tests/test_forms.py +1 -0
- nautobot/core/tests/test_jobs.py +460 -1
- nautobot/core/tests/test_models.py +9 -0
- nautobot/core/tests/test_settings_schema.py +7 -0
- nautobot/core/tests/test_tables.py +100 -0
- nautobot/core/tests/test_utils.py +63 -1
- nautobot/core/tests/test_views.py +30 -3
- nautobot/core/ui/nav.py +1 -0
- nautobot/core/ui/object_detail.py +15 -1
- nautobot/core/urls.py +11 -0
- nautobot/core/utils/lookup.py +11 -8
- nautobot/core/utils/querysets.py +64 -0
- nautobot/core/utils/requests.py +24 -9
- nautobot/core/views/__init__.py +42 -0
- nautobot/core/views/generic.py +131 -197
- nautobot/core/views/mixins.py +126 -38
- nautobot/core/views/renderers.py +6 -6
- nautobot/dcim/api/serializers.py +56 -64
- nautobot/dcim/api/views.py +47 -113
- nautobot/dcim/constants.py +6 -13
- nautobot/dcim/factory.py +6 -1
- nautobot/dcim/filters/__init__.py +31 -2
- nautobot/dcim/forms.py +36 -17
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
- nautobot/dcim/models/__init__.py +1 -1
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +22 -20
- nautobot/dcim/models/devices.py +10 -1
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +6 -5
- nautobot/dcim/models/racks.py +4 -4
- nautobot/dcim/tables/__init__.py +3 -3
- nautobot/dcim/tables/devices.py +7 -5
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller_create.html +1 -7
- nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
- nautobot/dcim/templates/dcim/device.html +1 -9
- nautobot/dcim/templates/dcim/device_edit.html +36 -37
- nautobot/dcim/templates/dcim/location.html +1 -9
- nautobot/dcim/templates/dcim/location_edit.html +1 -7
- nautobot/dcim/templates/dcim/rack.html +1 -9
- nautobot/dcim/templates/dcim/rack_edit.html +1 -7
- nautobot/dcim/templates/dcim/rackreservation.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
- nautobot/dcim/tests/integration/test_controller.py +62 -0
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +189 -0
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +181 -0
- nautobot/dcim/tests/test_api.py +16 -5
- nautobot/dcim/tests/test_filters.py +33 -0
- nautobot/dcim/tests/test_forms.py +51 -2
- nautobot/dcim/tests/test_graphql.py +52 -0
- nautobot/dcim/tests/test_jobs.py +118 -0
- nautobot/dcim/tests/test_models.py +52 -9
- nautobot/dcim/tests/test_views.py +21 -83
- nautobot/dcim/views.py +1 -13
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +90 -85
- nautobot/extras/api/views.py +22 -27
- nautobot/extras/constants.py +2 -0
- nautobot/extras/filters/__init__.py +8 -6
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +139 -31
- nautobot/extras/forms/mixins.py +14 -6
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +1 -2
- nautobot/extras/jobs.py +85 -18
- nautobot/extras/managers.py +3 -1
- nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
- nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +12 -11
- nautobot/extras/models/groups.py +11 -9
- nautobot/extras/models/jobs.py +23 -4
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/plugins/__init__.py +13 -2
- nautobot/extras/plugins/marketplace_manifest.yml +84 -79
- nautobot/extras/plugins/tables.py +16 -14
- nautobot/extras/plugins/views.py +65 -69
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/tables.py +7 -5
- nautobot/extras/templates/extras/dynamicgroup.html +1 -9
- nautobot/extras/templates/extras/job_detail.html +16 -0
- nautobot/extras/templates/extras/job_edit.html +1 -0
- nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
- nautobot/extras/templates/extras/marketplace.html +29 -11
- nautobot/extras/templates/extras/plugin_detail.html +32 -15
- nautobot/extras/templates/extras/plugins_tiles.html +21 -10
- nautobot/extras/templatetags/job_buttons.py +4 -4
- nautobot/extras/test_jobs/api_test_job.py +1 -1
- nautobot/extras/test_jobs/atomic_transaction.py +2 -2
- nautobot/extras/test_jobs/dry_run.py +1 -1
- nautobot/extras/test_jobs/fail.py +5 -5
- nautobot/extras/test_jobs/file_output.py +1 -1
- nautobot/extras/test_jobs/file_upload_fail.py +1 -1
- nautobot/extras/test_jobs/file_upload_pass.py +1 -1
- nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
- nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
- nautobot/extras/test_jobs/log_redaction.py +1 -1
- nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
- nautobot/extras/test_jobs/modify_db.py +1 -1
- nautobot/extras/test_jobs/object_var_optional.py +1 -1
- nautobot/extras/test_jobs/object_var_required.py +1 -1
- nautobot/extras/test_jobs/object_vars.py +1 -1
- nautobot/extras/test_jobs/pass.py +3 -3
- nautobot/extras/test_jobs/profiling.py +1 -1
- nautobot/extras/test_jobs/relative_import.py +3 -3
- nautobot/extras/test_jobs/singleton.py +16 -0
- nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
- nautobot/extras/test_jobs/task_queues.py +1 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
- nautobot/extras/tests/test_api.py +13 -13
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +2 -1
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +6 -6
- nautobot/extras/tests/test_forms.py +33 -1
- nautobot/extras/tests/test_jobs.py +178 -32
- nautobot/extras/tests/test_models.py +16 -10
- nautobot/extras/tests/test_plugins.py +62 -9
- nautobot/extras/tests/test_relationships.py +120 -9
- nautobot/extras/tests/test_views.py +56 -194
- nautobot/extras/utils.py +3 -2
- nautobot/extras/views.py +30 -98
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +41 -33
- nautobot/ipam/api/views.py +68 -117
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +3 -2
- nautobot/ipam/lookups.py +101 -62
- nautobot/ipam/models.py +66 -16
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +23 -7
- nautobot/ipam/templates/ipam/ipaddress.html +1 -9
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
- nautobot/ipam/templates/ipam/prefix.html +1 -9
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
- nautobot/ipam/templates/ipam/vlan.html +1 -9
- nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
- nautobot/ipam/tests/test_api.py +436 -3
- nautobot/ipam/tests/test_forms.py +49 -47
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +95 -34
- nautobot/ipam/tests/test_querysets.py +63 -1
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/utils/__init__.py +36 -6
- nautobot/ipam/views.py +61 -87
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/docs/404.html +46 -4
- nautobot/project-static/docs/apps/index.html +46 -4
- nautobot/project-static/docs/apps/nautobot-apps.html +47 -6
- nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +62 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +59 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +374 -122
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +90 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +95 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +53 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +52 -5
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +79 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +102 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +108 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +131 -38
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +239 -65
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +581 -165
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +109 -36
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +453 -167
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +493 -211
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +60 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +71 -15
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +407 -55
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +620 -205
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +858 -412
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +59 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +448 -186
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +365 -147
- nautobot/project-static/docs/development/apps/api/configuration-view.html +46 -4
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/global-search.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/graphql.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +68 -7
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +46 -4
- nautobot/project-static/docs/development/apps/api/prometheus.html +46 -4
- nautobot/project-static/docs/development/apps/api/setup.html +46 -4
- nautobot/project-static/docs/development/apps/api/testing.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/base-template.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/notes.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +52 -6
- nautobot/project-static/docs/development/apps/api/views/urls.html +46 -4
- nautobot/project-static/docs/development/apps/index.html +46 -4
- nautobot/project-static/docs/development/apps/migration/code-updates.html +46 -4
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +46 -4
- nautobot/project-static/docs/development/apps/migration/from-v1.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +46 -4
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +50 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +46 -4
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +211 -14
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +46 -4
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +46 -4
- nautobot/project-static/docs/development/core/application-registry.html +46 -4
- nautobot/project-static/docs/development/core/best-practices.html +46 -4
- nautobot/project-static/docs/development/core/bootstrap-ui.html +46 -4
- nautobot/project-static/docs/development/core/caching.html +46 -4
- nautobot/project-static/docs/development/core/controllers.html +46 -4
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +73 -74
- nautobot/project-static/docs/development/core/generic-views.html +46 -4
- nautobot/project-static/docs/development/core/getting-started.html +249 -224
- nautobot/project-static/docs/development/core/homepage.html +49 -7
- nautobot/project-static/docs/development/core/index.html +46 -4
- nautobot/project-static/docs/development/core/{local-k8s.html → minikube-dev-environment-for-k8s-jobs.html} +469 -168
- nautobot/project-static/docs/development/core/model-checklist.html +56 -12
- nautobot/project-static/docs/development/core/model-features.html +46 -4
- nautobot/project-static/docs/development/core/natural-keys.html +46 -4
- nautobot/project-static/docs/development/core/navigation-menu.html +46 -4
- nautobot/project-static/docs/development/core/release-checklist.html +49 -7
- nautobot/project-static/docs/development/core/role-internals.html +46 -4
- nautobot/project-static/docs/development/core/settings.html +46 -4
- nautobot/project-static/docs/development/core/style-guide.html +49 -7
- nautobot/project-static/docs/development/core/templates.html +46 -4
- nautobot/project-static/docs/development/core/testing.html +46 -4
- nautobot/project-static/docs/development/core/ui-component-framework.html +369 -273
- nautobot/project-static/docs/development/core/user-preferences.html +46 -4
- nautobot/project-static/docs/development/index.html +46 -4
- nautobot/project-static/docs/development/jobs/index.html +216 -122
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +46 -4
- nautobot/project-static/docs/index.html +54 -23
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +47 -7
- nautobot/project-static/docs/overview/design_philosophy.html +46 -4
- nautobot/project-static/docs/release-notes/index.html +52 -12
- nautobot/project-static/docs/release-notes/version-1.0.html +234 -193
- nautobot/project-static/docs/release-notes/version-1.1.html +231 -190
- nautobot/project-static/docs/release-notes/version-1.2.html +306 -265
- nautobot/project-static/docs/release-notes/version-1.3.html +332 -291
- nautobot/project-static/docs/release-notes/version-1.4.html +417 -377
- nautobot/project-static/docs/release-notes/version-1.5.html +605 -566
- nautobot/project-static/docs/release-notes/version-1.6.html +904 -447
- nautobot/project-static/docs/release-notes/version-2.0.html +528 -489
- nautobot/project-static/docs/release-notes/version-2.1.html +363 -324
- nautobot/project-static/docs/release-notes/version-2.2.html +356 -317
- nautobot/project-static/docs/release-notes/version-2.3.html +997 -352
- nautobot/project-static/docs/release-notes/version-2.4.html +525 -101
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +295 -287
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +48 -6
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +110 -8
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -6
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +66 -8
- nautobot/project-static/docs/user-guide/administration/installation/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +47 -5
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/services.html +46 -4
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +46 -4
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +46 -4
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +46 -4
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +49 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +50 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +49 -7
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +51 -7
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +49 -7
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
- nautobot/project-static/docs/user-guide/index.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +50 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +50 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +49 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +47 -5
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +94 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +74 -5
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +46 -4
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +9 -13
- nautobot/tenancy/views.py +4 -2
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +5 -4
- nautobot/users/api/views.py +3 -3
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/api/views.py +5 -24
- nautobot/virtualization/filters.py +20 -3
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
- nautobot/virtualization/tests/test_filters.py +17 -0
- nautobot/wireless/filters.py +2 -2
- nautobot/wireless/forms.py +1 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
- nautobot/wireless/tests/test_filters.py +29 -1
- nautobot/wireless/tests/test_views.py +22 -1
- nautobot/wireless/views.py +0 -10
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/METADATA +6 -6
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/RECORD +600 -550
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/WHEEL +1 -1
- nautobot/core/fixtures/user-data.json +0 -59
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/entry_points.txt +0 -0
nautobot/core/filters.py
CHANGED
|
@@ -13,6 +13,7 @@ from django_filters.constants import EMPTY_VALUES
|
|
|
13
13
|
from django_filters.utils import get_model_field, label_for_filter, resolve_field, verbose_lookup_expr
|
|
14
14
|
from drf_spectacular.types import OpenApiTypes
|
|
15
15
|
from drf_spectacular.utils import extend_schema_field
|
|
16
|
+
import timezone_field
|
|
16
17
|
|
|
17
18
|
from nautobot.core import constants, forms
|
|
18
19
|
from nautobot.core.forms import widgets
|
|
@@ -612,12 +613,16 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
612
613
|
models.UUIDField: {"filter_class": MultiValueUUIDFilter},
|
|
613
614
|
core_fields.MACAddressCharField: {"filter_class": MultiValueMACAddressFilter},
|
|
614
615
|
core_fields.TagsField: {"filter_class": TagFilter},
|
|
616
|
+
timezone_field.TimeZoneField: {"filter_class": MultiValueCharFilter},
|
|
615
617
|
}
|
|
616
618
|
)
|
|
617
619
|
|
|
620
|
+
USE_CHAR_FILTER_FOR_LOOKUPS = [django_filters.MultipleChoiceFilter]
|
|
621
|
+
|
|
618
622
|
@staticmethod
|
|
619
623
|
def _get_filter_lookup_dict(existing_filter):
|
|
620
624
|
# Choose the lookup expression map based on the filter type
|
|
625
|
+
|
|
621
626
|
if isinstance(
|
|
622
627
|
existing_filter,
|
|
623
628
|
(
|
|
@@ -637,6 +642,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
637
642
|
(
|
|
638
643
|
django_filters.ModelChoiceFilter,
|
|
639
644
|
django_filters.ModelMultipleChoiceFilter,
|
|
645
|
+
MultiValueUUIDFilter,
|
|
640
646
|
TagFilter,
|
|
641
647
|
TreeNodeMultipleChoiceFilter,
|
|
642
648
|
),
|
|
@@ -666,7 +672,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
666
672
|
the form `<field_name>__<lookup_expr>`
|
|
667
673
|
"""
|
|
668
674
|
magic_filters = {}
|
|
669
|
-
if filter_field.method is not None or filter_field.lookup_expr not in ["exact", "in"]:
|
|
675
|
+
if filter_field.method is not None or filter_field.lookup_expr not in ["exact", "in", "iexact"]:
|
|
670
676
|
return magic_filters
|
|
671
677
|
|
|
672
678
|
# Choose the lookup expression map based on the filter type
|
|
@@ -676,8 +682,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
676
682
|
return magic_filters
|
|
677
683
|
|
|
678
684
|
# Get properties of the existing filter for later use
|
|
679
|
-
|
|
680
|
-
field = get_model_field(cls._meta.model, field_name)
|
|
685
|
+
field = get_model_field(cls._meta.model, filter_field.field_name) # pylint: disable=no-member
|
|
681
686
|
|
|
682
687
|
# If there isn't a model field, return.
|
|
683
688
|
if field is None:
|
|
@@ -694,25 +699,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
694
699
|
new_filter_name = f"{filter_name}__{lookup_name}"
|
|
695
700
|
|
|
696
701
|
try:
|
|
697
|
-
|
|
698
|
-
# The filter field has been explicitly defined on the filterset class so we must manually
|
|
699
|
-
# create the new filter with the same type because there is no guarantee the defined type
|
|
700
|
-
# is the same as the default type for the field. This does not apply if the filter
|
|
701
|
-
# should retain the original lookup_expr type, such as `isnull` using a boolean field on a
|
|
702
|
-
# char or date object.
|
|
703
|
-
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
|
|
704
|
-
new_filter = type(filter_field)(
|
|
705
|
-
field_name=field_name,
|
|
706
|
-
lookup_expr=lookup_expr,
|
|
707
|
-
label=filter_field.label,
|
|
708
|
-
exclude=filter_field.exclude,
|
|
709
|
-
distinct=filter_field.distinct,
|
|
710
|
-
**filter_field.extra,
|
|
711
|
-
)
|
|
712
|
-
else:
|
|
713
|
-
# The filter field is listed in Meta.fields so we can safely rely on default behavior
|
|
714
|
-
# Will raise FieldLookupError if the lookup is invalid
|
|
715
|
-
new_filter = cls.filter_for_field(field, field_name, lookup_expr)
|
|
702
|
+
new_filter = cls._get_new_filter(filter_field, field, filter_name, lookup_expr)
|
|
716
703
|
except django_filters.exceptions.FieldLookupError:
|
|
717
704
|
# The filter could not be created because the lookup expression is not supported on the field
|
|
718
705
|
continue
|
|
@@ -725,7 +712,10 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
725
712
|
# If the base filter_field has a custom label, django_filters won't adjust it for the new_filter lookup,
|
|
726
713
|
# so we have to do it.
|
|
727
714
|
if filter_field.label and filter_field.label != label_for_filter(
|
|
728
|
-
cls.
|
|
715
|
+
cls._meta.model, # pylint: disable=no-member
|
|
716
|
+
filter_field.field_name,
|
|
717
|
+
filter_field.lookup_expr,
|
|
718
|
+
filter_field.exclude,
|
|
729
719
|
):
|
|
730
720
|
# Lightly adjusted from label_for_filter() implementation:
|
|
731
721
|
verbose_expression = ["exclude", filter_field.label] if new_filter.exclude else [filter_field.label]
|
|
@@ -738,6 +728,45 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
738
728
|
|
|
739
729
|
return magic_filters
|
|
740
730
|
|
|
731
|
+
@classmethod
|
|
732
|
+
def _should_use_char_filter_for_lookups(cls, filter_field):
|
|
733
|
+
return type(filter_field) in cls.USE_CHAR_FILTER_FOR_LOOKUPS
|
|
734
|
+
|
|
735
|
+
@classmethod
|
|
736
|
+
def _get_new_filter(cls, filter_field, field, filter_name, lookup_expr):
|
|
737
|
+
if cls._should_use_char_filter_for_lookups(filter_field):
|
|
738
|
+
# For some cases like `MultiValueChoiceFilter(django_filters.MultipleChoiceFilter)`
|
|
739
|
+
# we want to have choices field with no lookups and standard char field for lookups filtering.
|
|
740
|
+
# Using a `choice` field for lookups blocks us from using `__re`, `__iew` or other "partial" filters.
|
|
741
|
+
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
|
|
742
|
+
return MultiValueCharFilter(
|
|
743
|
+
field_name=filter_field.field_name,
|
|
744
|
+
lookup_expr=lookup_expr,
|
|
745
|
+
label=filter_field.label,
|
|
746
|
+
exclude=filter_field.exclude,
|
|
747
|
+
distinct=filter_field.distinct,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
if filter_name in cls.declared_filters and lookup_expr not in {"isnull"}: # pylint: disable=no-member
|
|
751
|
+
# The filter field has been explicitly defined on the filterset class so we must manually
|
|
752
|
+
# create the new filter with the same type because there is no guarantee the defined type
|
|
753
|
+
# is the same as the default type for the field. This does not apply if the filter
|
|
754
|
+
# should retain the original lookup_expr type, such as `isnull` using a boolean field on a
|
|
755
|
+
# char or date object.
|
|
756
|
+
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
|
|
757
|
+
return type(filter_field)(
|
|
758
|
+
field_name=filter_field.field_name,
|
|
759
|
+
lookup_expr=lookup_expr,
|
|
760
|
+
label=filter_field.label,
|
|
761
|
+
exclude=filter_field.exclude,
|
|
762
|
+
distinct=filter_field.distinct,
|
|
763
|
+
**filter_field.extra,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
# The filter field is listed in Meta.fields so we can safely rely on default behavior
|
|
767
|
+
# Will raise FieldLookupError if the lookup is invalid
|
|
768
|
+
return cls.filter_for_field(field, filter_field.field_name, lookup_expr)
|
|
769
|
+
|
|
741
770
|
@classmethod
|
|
742
771
|
def add_filter(cls, new_filter_name, new_filter_field):
|
|
743
772
|
"""
|
|
@@ -748,22 +777,22 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
748
777
|
if not isinstance(new_filter_field, django_filters.Filter):
|
|
749
778
|
raise TypeError(f"Tried to add filter ({new_filter_name}) which is not an instance of Django Filter")
|
|
750
779
|
|
|
751
|
-
if new_filter_name in cls.base_filters:
|
|
780
|
+
if new_filter_name in cls.base_filters: # pylint: disable=no-member
|
|
752
781
|
raise AttributeError(
|
|
753
782
|
f"There was a conflict with filter `{new_filter_name}`, the custom filter was ignored."
|
|
754
783
|
)
|
|
755
784
|
|
|
756
|
-
cls.base_filters[new_filter_name] = new_filter_field
|
|
785
|
+
cls.base_filters[new_filter_name] = new_filter_field # pylint: disable=no-member
|
|
757
786
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
758
|
-
if cls._meta.model is not None:
|
|
759
|
-
cls.base_filters.update(
|
|
787
|
+
if cls._meta.model is not None: # pylint: disable=no-member
|
|
788
|
+
cls.base_filters.update( # pylint: disable=no-member
|
|
760
789
|
cls._generate_lookup_expression_filters(filter_name=new_filter_name, filter_field=new_filter_field)
|
|
761
790
|
)
|
|
762
791
|
|
|
763
792
|
@classmethod
|
|
764
793
|
def get_fields(cls):
|
|
765
794
|
fields = super().get_fields()
|
|
766
|
-
if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude):
|
|
795
|
+
if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude): # pylint: disable=no-member
|
|
767
796
|
# Add "id" as the first key in the `fields` dict
|
|
768
797
|
fields = {"id": [django_filters.conf.settings.DEFAULT_LOOKUP_EXPR], **fields}
|
|
769
798
|
return fields
|
|
@@ -780,7 +809,7 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
780
809
|
if filter_name.startswith("_"):
|
|
781
810
|
del filters[filter_name]
|
|
782
811
|
|
|
783
|
-
if getattr(cls._meta.model, "is_contact_associable_model", False):
|
|
812
|
+
if getattr(cls._meta.model, "is_contact_associable_model", False): # pylint: disable=no-member
|
|
784
813
|
# Add "contacts" and "teams" filters
|
|
785
814
|
from nautobot.extras.models import Contact, Team
|
|
786
815
|
|
|
@@ -800,13 +829,13 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
800
829
|
label="Teams (name or ID)",
|
|
801
830
|
)
|
|
802
831
|
|
|
803
|
-
if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False):
|
|
804
|
-
if not hasattr(cls._meta.model, "static_group_association_set"):
|
|
832
|
+
if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False): # pylint: disable=no-member
|
|
833
|
+
if not hasattr(cls._meta.model, "static_group_association_set"): # pylint: disable=no-member
|
|
805
834
|
logger.warning(
|
|
806
835
|
"Model %s has 'is_dynamic_group_associable_model = True' but lacks "
|
|
807
836
|
"a 'static_group_association_set' attribute. Perhaps this is due to it inheriting from "
|
|
808
837
|
"the deprecated DynamicGroupMixin class instead of the preferred DynamicGroupsModelMixin?",
|
|
809
|
-
cls._meta.model,
|
|
838
|
+
cls._meta.model, # pylint: disable=no-member
|
|
810
839
|
)
|
|
811
840
|
else:
|
|
812
841
|
# Add "dynamic_groups" field as the last key
|
|
@@ -816,14 +845,14 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
816
845
|
queryset=DynamicGroup.objects.all(),
|
|
817
846
|
field_name="static_group_association_set__dynamic_group",
|
|
818
847
|
to_field_name="name",
|
|
819
|
-
query_params={"content_type": cls._meta.model._meta.label_lower},
|
|
848
|
+
query_params={"content_type": cls._meta.model._meta.label_lower}, # pylint: disable=no-member
|
|
820
849
|
label="Dynamic groups (name or ID)",
|
|
821
850
|
)
|
|
822
851
|
|
|
823
852
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
824
|
-
if cls._meta.model is not None:
|
|
853
|
+
if cls._meta.model is not None: # pylint: disable=no-member
|
|
825
854
|
if "tags" in filters and isinstance(filters["tags"], TagFilter):
|
|
826
|
-
filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]}
|
|
855
|
+
filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]} # pylint: disable=no-member
|
|
827
856
|
|
|
828
857
|
new_filters = {}
|
|
829
858
|
for existing_filter_name, existing_filter in filters.items():
|
|
@@ -845,7 +874,10 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
845
874
|
Note: Any CharField or IntegerField with choices set is a ChoiceField.
|
|
846
875
|
"""
|
|
847
876
|
if lookup_type == "exact" and getattr(field, "choices", None):
|
|
877
|
+
if isinstance(field, timezone_field.TimeZoneField):
|
|
878
|
+
return django_filters.MultipleChoiceFilter, {"choices": ((str(v), n) for v, n in field.choices)}
|
|
848
879
|
return django_filters.MultipleChoiceFilter, {"choices": field.choices}
|
|
880
|
+
|
|
849
881
|
return super().filter_for_lookup(field, lookup_type)
|
|
850
882
|
|
|
851
883
|
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
|
nautobot/core/forms/__init__.py
CHANGED
|
@@ -76,24 +76,21 @@ from nautobot.core.forms.widgets import (
|
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
__all__ = (
|
|
79
|
-
"add_blank_choice",
|
|
80
|
-
"add_field_to_filter_form_class",
|
|
81
|
-
"AddressFieldMixin",
|
|
82
79
|
"ALPHANUMERIC_EXPANSION_PATTERN",
|
|
80
|
+
"BOOLEAN_CHOICES",
|
|
81
|
+
"BOOLEAN_WITH_BLANK_CHOICES",
|
|
82
|
+
"IP4_EXPANSION_PATTERN",
|
|
83
|
+
"IP6_EXPANSION_PATTERN",
|
|
84
|
+
"NUMERIC_EXPANSION_PATTERN",
|
|
83
85
|
"APISelect",
|
|
84
86
|
"APISelectMultiple",
|
|
87
|
+
"AddressFieldMixin",
|
|
85
88
|
"AutoPositionField",
|
|
86
89
|
"AutoPositionPatternField",
|
|
87
|
-
"BOOLEAN_CHOICES",
|
|
88
|
-
"BOOLEAN_WITH_BLANK_CHOICES",
|
|
89
90
|
"BootstrapMixin",
|
|
90
91
|
"BulkEditForm",
|
|
91
92
|
"BulkEditNullBooleanSelect",
|
|
92
93
|
"BulkRenameForm",
|
|
93
|
-
"ColorSelect",
|
|
94
|
-
"CommentField",
|
|
95
|
-
"ConfirmationForm",
|
|
96
|
-
"ContentTypeSelect",
|
|
97
94
|
"CSVChoiceField",
|
|
98
95
|
"CSVContentTypeField",
|
|
99
96
|
"CSVDataField",
|
|
@@ -102,34 +99,29 @@ __all__ = (
|
|
|
102
99
|
"CSVModelForm",
|
|
103
100
|
"CSVMultipleChoiceField",
|
|
104
101
|
"CSVMultipleContentTypeField",
|
|
102
|
+
"ColorSelect",
|
|
103
|
+
"CommentField",
|
|
104
|
+
"ConfirmationForm",
|
|
105
|
+
"ContentTypeSelect",
|
|
105
106
|
"DatePicker",
|
|
106
107
|
"DateTimePicker",
|
|
107
108
|
"DynamicFilterForm",
|
|
108
109
|
"DynamicModelChoiceField",
|
|
109
110
|
"DynamicModelMultipleChoiceField",
|
|
110
|
-
"expand_alphanumeric_pattern",
|
|
111
|
-
"expand_ipaddress_pattern",
|
|
112
111
|
"ExpandableIPAddressField",
|
|
113
112
|
"ExpandableNameField",
|
|
114
|
-
"form_from_model",
|
|
115
113
|
"ImportForm",
|
|
116
|
-
"IP4_EXPANSION_PATTERN",
|
|
117
|
-
"IP6_EXPANSION_PATTERN",
|
|
118
114
|
"JSONArrayFormField",
|
|
119
115
|
"JSONField",
|
|
120
116
|
"LaxURLField",
|
|
121
117
|
"MACAddressField",
|
|
122
118
|
"MultiMatchModelMultipleChoiceField",
|
|
123
|
-
"MultipleContentTypeField",
|
|
124
119
|
"MultiValueCharField",
|
|
125
120
|
"MultiValueCharInput",
|
|
121
|
+
"MultipleContentTypeField",
|
|
126
122
|
"NullableDateField",
|
|
127
|
-
"NUMERIC_EXPANSION_PATTERN",
|
|
128
123
|
"NumericArrayField",
|
|
129
|
-
"parse_alphanumeric_range",
|
|
130
|
-
"parse_numeric_range",
|
|
131
124
|
"PrefixFieldMixin",
|
|
132
|
-
"restrict_form_fields",
|
|
133
125
|
"ReturnURLForm",
|
|
134
126
|
"SearchForm",
|
|
135
127
|
"SelectWithDisabled",
|
|
@@ -142,4 +134,12 @@ __all__ = (
|
|
|
142
134
|
"TableConfigForm",
|
|
143
135
|
"TagFilterField",
|
|
144
136
|
"TimePicker",
|
|
137
|
+
"add_blank_choice",
|
|
138
|
+
"add_field_to_filter_form_class",
|
|
139
|
+
"expand_alphanumeric_pattern",
|
|
140
|
+
"expand_ipaddress_pattern",
|
|
141
|
+
"form_from_model",
|
|
142
|
+
"parse_alphanumeric_range",
|
|
143
|
+
"parse_numeric_range",
|
|
144
|
+
"restrict_form_fields",
|
|
145
145
|
)
|
nautobot/core/forms/fields.py
CHANGED
|
@@ -22,7 +22,6 @@ from nautobot.core.models import validators
|
|
|
22
22
|
from nautobot.core.utils import data as data_utils, lookup
|
|
23
23
|
|
|
24
24
|
__all__ = (
|
|
25
|
-
"CommentField",
|
|
26
25
|
"CSVChoiceField",
|
|
27
26
|
"CSVContentTypeField",
|
|
28
27
|
"CSVDataField",
|
|
@@ -30,16 +29,17 @@ __all__ = (
|
|
|
30
29
|
"CSVModelChoiceField",
|
|
31
30
|
"CSVMultipleChoiceField",
|
|
32
31
|
"CSVMultipleContentTypeField",
|
|
32
|
+
"CommentField",
|
|
33
33
|
"DynamicModelChoiceField",
|
|
34
34
|
"DynamicModelMultipleChoiceField",
|
|
35
35
|
"ExpandableIPAddressField",
|
|
36
36
|
"ExpandableNameField",
|
|
37
|
-
"JSONField",
|
|
38
37
|
"JSONArrayFormField",
|
|
38
|
+
"JSONField",
|
|
39
39
|
"LaxURLField",
|
|
40
40
|
"MACAddressField",
|
|
41
|
-
"MultipleContentTypeField",
|
|
42
41
|
"MultiMatchModelMultipleChoiceField",
|
|
42
|
+
"MultipleContentTypeField",
|
|
43
43
|
"NumericArrayField",
|
|
44
44
|
"SlugField",
|
|
45
45
|
"TagFilterField",
|
|
@@ -102,13 +102,13 @@ class CSVFileField(django_forms.FileField):
|
|
|
102
102
|
"in double quotes."
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
def to_python(self,
|
|
105
|
+
def to_python(self, data):
|
|
106
106
|
"""For parity with CSVDataField, this returns the CSV text rather than an UploadedFile object."""
|
|
107
|
-
if
|
|
107
|
+
if data is None:
|
|
108
108
|
return None
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
return
|
|
110
|
+
data = super().to_python(data)
|
|
111
|
+
return data.read().decode("utf-8-sig").strip()
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
class CSVChoiceField(django_forms.ChoiceField):
|
|
@@ -504,6 +504,8 @@ class DynamicModelChoiceMixin:
|
|
|
504
504
|
):
|
|
505
505
|
self.display_field = display_field
|
|
506
506
|
self.query_params = query_params or {}
|
|
507
|
+
# Default to "exclude_m2m=true" for improved performance, if not otherwise specified
|
|
508
|
+
self.query_params.setdefault("exclude_m2m", "true")
|
|
507
509
|
self.initial_params = initial_params or {}
|
|
508
510
|
self.null_option = null_option
|
|
509
511
|
self.disabled_indicator = disabled_indicator
|
|
@@ -536,9 +538,10 @@ class DynamicModelChoiceMixin:
|
|
|
536
538
|
# Toggle depth
|
|
537
539
|
attrs["data-depth"] = self.depth
|
|
538
540
|
|
|
539
|
-
# Attach any static query parameters
|
|
540
|
-
|
|
541
|
-
|
|
541
|
+
# Attach any static query parameters if supported
|
|
542
|
+
if isinstance(widget, widgets.APISelect) or hasattr(widget, "add_query_param"):
|
|
543
|
+
for key, value in self.query_params.items():
|
|
544
|
+
widget.add_query_param(key, value)
|
|
542
545
|
|
|
543
546
|
return attrs
|
|
544
547
|
|
|
@@ -819,7 +822,7 @@ class MultiMatchModelMultipleChoiceField(DynamicModelChoiceMixin, django_filters
|
|
|
819
822
|
self.natural_key = kwargs.setdefault("to_field_name", "slug")
|
|
820
823
|
super().__init__(*args, **kwargs)
|
|
821
824
|
|
|
822
|
-
def _check_values(self, values):
|
|
825
|
+
def _check_values(self, values): # pylint:disable=arguments-renamed
|
|
823
826
|
"""
|
|
824
827
|
This method overloads the grandparent method in `django.forms.models.ModelMultipleChoiceField`,
|
|
825
828
|
re-using some of that method's existing logic and adding support for coupling this field with
|
nautobot/core/forms/forms.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import json
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
4
5
|
|
|
5
6
|
from django import forms
|
|
7
|
+
from django.core.exceptions import FieldDoesNotExist
|
|
8
|
+
from django.db.models.fields.related import ManyToManyField
|
|
6
9
|
from django.forms import formset_factory
|
|
7
10
|
from django.urls import reverse
|
|
8
11
|
import yaml
|
|
@@ -15,8 +18,8 @@ __all__ = (
|
|
|
15
18
|
"BootstrapMixin",
|
|
16
19
|
"BulkEditForm",
|
|
17
20
|
"BulkRenameForm",
|
|
18
|
-
"ConfirmationForm",
|
|
19
21
|
"CSVModelForm",
|
|
22
|
+
"ConfirmationForm",
|
|
20
23
|
"DynamicFilterForm",
|
|
21
24
|
"ImportForm",
|
|
22
25
|
"PrefixFieldMixin",
|
|
@@ -124,7 +127,35 @@ class BulkEditForm(forms.Form):
|
|
|
124
127
|
|
|
125
128
|
if edit_all:
|
|
126
129
|
self.fields["pk"].required = False
|
|
127
|
-
self.fields["_all"] = forms.BooleanField(widget=forms.HiddenInput(), required=
|
|
130
|
+
self.fields["_all"] = forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=True)
|
|
131
|
+
|
|
132
|
+
def _save_m2m_fields(self, obj):
|
|
133
|
+
"""Save M2M fields"""
|
|
134
|
+
from nautobot.core.models.fields import TagsField # Avoid circular dependency
|
|
135
|
+
|
|
136
|
+
m2m_field_names = []
|
|
137
|
+
# Handle M2M Save
|
|
138
|
+
for key in self.cleaned_data.keys():
|
|
139
|
+
if key.startswith(("add_", "remove_")):
|
|
140
|
+
field_name = key.lstrip("add_")
|
|
141
|
+
if field_name in m2m_field_names:
|
|
142
|
+
continue
|
|
143
|
+
with contextlib.suppress(FieldDoesNotExist):
|
|
144
|
+
field = obj._meta.get_field(field_name)
|
|
145
|
+
is_m2m_field = isinstance(field, (ManyToManyField, TagsField))
|
|
146
|
+
if is_m2m_field:
|
|
147
|
+
m2m_field_names.append(field_name)
|
|
148
|
+
|
|
149
|
+
for field_name in m2m_field_names:
|
|
150
|
+
m2m_field = getattr(obj, field_name)
|
|
151
|
+
if self.cleaned_data.get(f"add_{field_name}", None):
|
|
152
|
+
m2m_field.add(*self.cleaned_data[f"add_{field_name}"])
|
|
153
|
+
if self.cleaned_data.get(f"remove_{field_name}", None):
|
|
154
|
+
m2m_field.remove(*self.cleaned_data[f"remove_{field_name}"])
|
|
155
|
+
|
|
156
|
+
def post_save(self, obj):
|
|
157
|
+
"""Post save action"""
|
|
158
|
+
self._save_m2m_fields(obj)
|
|
128
159
|
|
|
129
160
|
|
|
130
161
|
class BulkRenameForm(forms.Form):
|
nautobot/core/graphql/types.py
CHANGED
|
@@ -10,7 +10,7 @@ class OptimizedNautobotObjectType(gql_optimizer.OptimizedDjangoObjectType):
|
|
|
10
10
|
url = graphene.String()
|
|
11
11
|
|
|
12
12
|
def resolve_url(self, info):
|
|
13
|
-
return self.get_absolute_url(api=True)
|
|
13
|
+
return self.get_absolute_url(api=True) # pylint: disable=no-member
|
|
14
14
|
|
|
15
15
|
class Meta:
|
|
16
16
|
abstract = True
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -4,7 +4,9 @@ from io import BytesIO
|
|
|
4
4
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib.contenttypes.models import ContentType
|
|
7
|
-
from django.core.exceptions import
|
|
7
|
+
from django.core.exceptions import (
|
|
8
|
+
PermissionDenied,
|
|
9
|
+
)
|
|
8
10
|
from django.db import transaction
|
|
9
11
|
from django.http import QueryDict
|
|
10
12
|
from rest_framework import exceptions as drf_exceptions
|
|
@@ -15,6 +17,7 @@ from nautobot.core.api.renderers import NautobotCSVRenderer
|
|
|
15
17
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
16
18
|
from nautobot.core.celery import app, register_jobs
|
|
17
19
|
from nautobot.core.exceptions import AbortTransaction
|
|
20
|
+
from nautobot.core.jobs.bulk_actions import BulkDeleteObjects, BulkEditObjects
|
|
18
21
|
from nautobot.core.jobs.cleanup import LogsCleanup
|
|
19
22
|
from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
|
|
20
23
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
@@ -25,7 +28,16 @@ from nautobot.extras.datasources import (
|
|
|
25
28
|
refresh_datasource_content,
|
|
26
29
|
refresh_job_code_from_repository,
|
|
27
30
|
)
|
|
28
|
-
from nautobot.extras.jobs import
|
|
31
|
+
from nautobot.extras.jobs import (
|
|
32
|
+
BooleanVar,
|
|
33
|
+
ChoiceVar,
|
|
34
|
+
FileVar,
|
|
35
|
+
Job,
|
|
36
|
+
ObjectVar,
|
|
37
|
+
RunJobTaskFailed,
|
|
38
|
+
StringVar,
|
|
39
|
+
TextVar,
|
|
40
|
+
)
|
|
29
41
|
from nautobot.extras.models import ExportTemplate, GitRepository
|
|
30
42
|
|
|
31
43
|
name = "System Jobs"
|
|
@@ -47,7 +59,7 @@ class GitRepositorySync(Job):
|
|
|
47
59
|
description = "Clone and/or pull a Git repository, then refresh data sourced from this repository."
|
|
48
60
|
has_sensitive_variables = False
|
|
49
61
|
|
|
50
|
-
def run(self, repository):
|
|
62
|
+
def run(self, repository): # pylint:disable=arguments-differ
|
|
51
63
|
job_result = self.job_result
|
|
52
64
|
user = job_result.user
|
|
53
65
|
|
|
@@ -88,7 +100,7 @@ class GitRepositoryDryRun(Job):
|
|
|
88
100
|
description = "Dry run of Git repository sync - will not update data sourced from this repository."
|
|
89
101
|
has_sensitive_variables = False
|
|
90
102
|
|
|
91
|
-
def run(self, repository):
|
|
103
|
+
def run(self, repository): # pylint:disable=arguments-differ
|
|
92
104
|
job_result = self.job_result
|
|
93
105
|
self.logger.info(f'Performing a Dry Run on Git repository "{repository.name}"...')
|
|
94
106
|
|
|
@@ -138,7 +150,7 @@ class ExportObjectList(Job):
|
|
|
138
150
|
soft_time_limit = 1800
|
|
139
151
|
time_limit = 2000
|
|
140
152
|
|
|
141
|
-
def run(self, *, content_type, query_string="", export_format="csv", export_template=None):
|
|
153
|
+
def run(self, *, content_type, query_string="", export_format="csv", export_template=None): # pylint:disable=arguments-differ
|
|
142
154
|
if not self.user.has_perm(f"{content_type.app_label}.view_{content_type.model}"):
|
|
143
155
|
self.logger.error('User "%s" does not have permission to view %s objects', self.user, content_type.model)
|
|
144
156
|
raise PermissionDenied("User does not have view permissions on the requested content-type")
|
|
@@ -286,7 +298,7 @@ class ImportObjects(Job):
|
|
|
286
298
|
self.logger.error("Row %d: `%s`: `%s`", row, field, err[0])
|
|
287
299
|
return new_objs, validation_failed
|
|
288
300
|
|
|
289
|
-
def run(self, *, content_type, csv_data=None, csv_file=None, roll_back_if_error=False):
|
|
301
|
+
def run(self, *, content_type, csv_data=None, csv_file=None, roll_back_if_error=False): # pylint:disable=arguments-differ
|
|
290
302
|
if not self.user.has_perm(f"{content_type.app_label}.add_{content_type.model}"):
|
|
291
303
|
self.logger.error('User "%s" does not have permission to create %s objects', self.user, content_type.model)
|
|
292
304
|
raise PermissionDenied("User does not have create permissions on the requested content-type")
|
|
@@ -347,5 +359,14 @@ class ImportObjects(Job):
|
|
|
347
359
|
raise RunJobTaskFailed("CSV import not fully successful, see logs")
|
|
348
360
|
|
|
349
361
|
|
|
350
|
-
jobs = [
|
|
362
|
+
jobs = [
|
|
363
|
+
BulkDeleteObjects,
|
|
364
|
+
BulkEditObjects,
|
|
365
|
+
ExportObjectList,
|
|
366
|
+
GitRepositorySync,
|
|
367
|
+
GitRepositoryDryRun,
|
|
368
|
+
ImportObjects,
|
|
369
|
+
LogsCleanup,
|
|
370
|
+
RefreshDynamicGroupCaches,
|
|
371
|
+
]
|
|
351
372
|
register_jobs(*jobs)
|