nautobot 2.2.9__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/forms.py +4 -0
- nautobot/apps/models.py +10 -1
- nautobot/circuits/__init__.py +0 -1
- nautobot/circuits/apps.py +1 -0
- nautobot/circuits/factory.py +15 -3
- nautobot/circuits/filters.py +13 -0
- nautobot/circuits/forms.py +13 -0
- nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
- nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
- nautobot/circuits/models.py +16 -3
- nautobot/circuits/tables.py +16 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
- nautobot/circuits/tests/test_api.py +7 -5
- nautobot/circuits/tests/test_filters.py +12 -5
- nautobot/circuits/tests/test_models.py +33 -2
- nautobot/circuits/views.py +2 -3
- nautobot/cloud/__init__.py +0 -0
- nautobot/cloud/api/__init__.py +0 -0
- nautobot/cloud/api/serializers.py +54 -0
- nautobot/cloud/api/urls.py +16 -0
- nautobot/cloud/api/views.py +48 -0
- nautobot/cloud/apps.py +13 -0
- nautobot/cloud/factory.py +113 -0
- nautobot/cloud/filters.py +187 -0
- nautobot/cloud/forms.py +339 -0
- nautobot/cloud/homepage.py +43 -0
- nautobot/cloud/migrations/0001_initial.py +304 -0
- nautobot/cloud/migrations/__init__.py +0 -0
- nautobot/cloud/models.py +246 -0
- nautobot/cloud/navigation.py +85 -0
- nautobot/cloud/tables.py +157 -0
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -0
- nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
- nautobot/cloud/tests/__init__.py +0 -0
- nautobot/cloud/tests/test_api.py +248 -0
- nautobot/cloud/tests/test_filters.py +125 -0
- nautobot/cloud/tests/test_models.py +43 -0
- nautobot/cloud/tests/test_views.py +153 -0
- nautobot/cloud/urls.py +14 -0
- nautobot/cloud/views.py +181 -0
- nautobot/core/__init__.py +0 -3
- nautobot/core/api/metadata.py +1 -0
- nautobot/core/api/parsers.py +7 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/utils.py +1 -0
- nautobot/core/api/views.py +4 -0
- nautobot/core/apps/__init__.py +6 -3
- nautobot/core/constants.py +8 -0
- nautobot/core/factory.py +32 -1
- nautobot/core/filters.py +95 -13
- nautobot/core/forms/fields.py +10 -4
- nautobot/core/forms/forms.py +11 -3
- nautobot/core/forms/widgets.py +18 -1
- nautobot/core/graphql/schema.py +26 -4
- nautobot/core/jobs/__init__.py +16 -2
- nautobot/core/jobs/cleanup.py +100 -0
- nautobot/core/jobs/groups.py +38 -0
- nautobot/core/management/commands/generate_test_data.py +116 -3
- nautobot/core/models/__init__.py +34 -9
- nautobot/core/models/generics.py +19 -3
- nautobot/core/models/name_color_content_types.py +7 -28
- nautobot/core/models/querysets.py +4 -3
- nautobot/core/models/tree_queries.py +1 -1
- nautobot/core/models/utils.py +21 -5
- nautobot/core/settings.py +2 -17
- nautobot/core/settings.yaml +34 -13
- nautobot/core/settings_funcs.py +103 -0
- nautobot/core/tables.py +130 -56
- nautobot/core/templates/admin/search_form.html +1 -1
- nautobot/core/templates/buttons/add.html +11 -3
- nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
- nautobot/core/templates/buttons/export.html +101 -53
- nautobot/core/templates/buttons/job_import.html +11 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
- nautobot/core/templates/generic/object_bulk_update.html +3 -1
- nautobot/core/templates/generic/object_changelog.html +0 -9
- nautobot/core/templates/generic/object_list.html +156 -17
- nautobot/core/templates/generic/object_retrieve.html +80 -16
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
- nautobot/core/templates/inc/javascript.html +2 -0
- nautobot/core/templates/inc/media.html +2 -2
- nautobot/core/templates/inc/nav_menu.html +1 -0
- nautobot/core/templates/inc/paginator.html +7 -7
- nautobot/core/templates/inc/search_panel.html +2 -2
- nautobot/core/templates/inc/table.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +13 -8
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
- nautobot/core/templates/utilities/theme_preview.html +25 -8
- nautobot/core/templates/utilities/worker_status.html +152 -0
- nautobot/core/templatetags/buttons.py +335 -38
- nautobot/core/templatetags/form_helpers.py +1 -1
- nautobot/core/templatetags/helpers.py +181 -11
- nautobot/core/testing/api.py +5 -4
- nautobot/core/testing/filters.py +63 -14
- nautobot/core/testing/mixins.py +46 -0
- nautobot/core/testing/models.py +22 -0
- nautobot/core/testing/schema.py +4 -8
- nautobot/core/testing/views.py +31 -14
- nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
- nautobot/core/tests/integration/test_swagger.py +1 -1
- nautobot/core/tests/nautobot_config.py +0 -1
- nautobot/core/tests/runner.py +2 -2
- nautobot/core/tests/test_api.py +1 -0
- nautobot/core/tests/test_authentication.py +7 -2
- nautobot/core/tests/test_filters.py +11 -9
- nautobot/core/tests/test_forms.py +9 -0
- nautobot/core/tests/test_graphql.py +27 -16
- nautobot/core/tests/test_jobs.py +122 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +12 -5
- nautobot/core/tests/test_utils.py +31 -20
- nautobot/core/tests/test_views.py +6 -6
- nautobot/core/urls.py +8 -3
- nautobot/core/utils/deprecation.py +29 -0
- nautobot/core/utils/filtering.py +12 -9
- nautobot/core/utils/lookup.py +37 -2
- nautobot/core/utils/requests.py +4 -1
- nautobot/core/views/__init__.py +137 -24
- nautobot/core/views/generic.py +119 -67
- nautobot/core/views/mixins.py +105 -36
- nautobot/core/views/paginator.py +9 -3
- nautobot/core/views/renderers.py +121 -56
- nautobot/core/views/utils.py +81 -1
- nautobot/dcim/__init__.py +0 -1
- nautobot/dcim/api/serializers.py +180 -44
- nautobot/dcim/api/urls.py +7 -3
- nautobot/dcim/api/views.py +53 -7
- nautobot/dcim/apps.py +3 -0
- nautobot/dcim/choices.py +25 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/factory.py +252 -18
- nautobot/dcim/filters/__init__.py +373 -193
- nautobot/dcim/filters/mixins.py +274 -1
- nautobot/dcim/forms.py +834 -121
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/homepage.py +1 -1
- nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
- nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
- nautobot/dcim/migrations/0061_module_models.py +862 -0
- nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
- nautobot/dcim/models/__init__.py +8 -0
- nautobot/dcim/models/cables.py +15 -0
- nautobot/dcim/models/device_component_templates.py +207 -53
- nautobot/dcim/models/device_components.py +275 -99
- nautobot/dcim/models/devices.py +468 -13
- nautobot/dcim/models/racks.py +0 -1
- nautobot/dcim/navigation.py +47 -0
- nautobot/dcim/signals.py +3 -3
- nautobot/dcim/tables/__init__.py +35 -23
- nautobot/dcim/tables/devices.py +229 -43
- nautobot/dcim/tables/devicetypes.py +65 -9
- nautobot/dcim/tables/racks.py +5 -1
- nautobot/dcim/tables/template_code.py +46 -26
- nautobot/dcim/templates/dcim/cable_connect.html +76 -3
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/device/base.html +14 -6
- nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
- nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
- nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
- nautobot/dcim/templates/dcim/device/frontports.html +2 -3
- nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
- nautobot/dcim/templates/dcim/device/inventory.html +2 -3
- nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
- nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
- nautobot/dcim/templates/dcim/device/powerports.html +2 -3
- nautobot/dcim/templates/dcim/device/rearports.html +2 -3
- nautobot/dcim/templates/dcim/device.html +45 -1
- nautobot/dcim/templates/dcim/device_component.html +13 -5
- nautobot/dcim/templates/dcim/device_list.html +2 -1
- nautobot/dcim/templates/dcim/devicetype.html +99 -98
- nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
- nautobot/dcim/templates/dcim/interface.html +17 -2
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/manufacturer.html +24 -0
- nautobot/dcim/templates/dcim/module/base.html +97 -0
- nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
- nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
- nautobot/dcim/templates/dcim/module_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_frontports.html +53 -0
- nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
- nautobot/dcim/templates/dcim/module_list.html +20 -0
- nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
- nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
- nautobot/dcim/templates/dcim/module_powerports.html +53 -0
- nautobot/dcim/templates/dcim/module_rearports.html +53 -0
- nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
- nautobot/dcim/templates/dcim/module_update.html +71 -0
- nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
- nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
- nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
- nautobot/dcim/tests/test_api.py +693 -208
- nautobot/dcim/tests/test_filters.py +843 -217
- nautobot/dcim/tests/test_models.py +1072 -8
- nautobot/dcim/tests/test_views.py +1510 -341
- nautobot/dcim/urls.py +17 -2
- nautobot/dcim/utils.py +2 -3
- nautobot/dcim/views.py +1106 -116
- nautobot/extras/__init__.py +0 -1
- nautobot/extras/api/serializers.py +115 -3
- nautobot/extras/api/urls.py +12 -0
- nautobot/extras/api/views.py +66 -0
- nautobot/extras/apps.py +2 -2
- nautobot/extras/choices.py +43 -0
- nautobot/extras/context_managers.py +13 -8
- nautobot/extras/datasources/git.py +2 -0
- nautobot/extras/factory.py +460 -9
- nautobot/extras/filters/__init__.py +174 -3
- nautobot/extras/filters/mixins.py +46 -43
- nautobot/extras/forms/base.py +24 -5
- nautobot/extras/forms/forms.py +227 -8
- nautobot/extras/forms/mixins.py +93 -0
- nautobot/extras/graphql/types.py +23 -10
- nautobot/extras/homepage.py +14 -1
- nautobot/extras/management/__init__.py +1 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
- nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
- nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
- nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
- nautobot/extras/migrations/0111_metadata.py +162 -0
- nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
- nautobot/extras/migrations/0113_saved_views.py +77 -0
- nautobot/extras/models/__init__.py +15 -1
- nautobot/extras/models/change_logging.py +3 -3
- nautobot/extras/models/contacts.py +4 -0
- nautobot/extras/models/customfields.py +18 -3
- nautobot/extras/models/groups.py +389 -225
- nautobot/extras/models/jobs.py +6 -3
- nautobot/extras/models/metadata.py +441 -0
- nautobot/extras/models/mixins.py +72 -62
- nautobot/extras/models/models.py +118 -9
- nautobot/extras/models/relationships.py +9 -2
- nautobot/extras/models/tags.py +13 -2
- nautobot/extras/navigation.py +57 -0
- nautobot/extras/plugins/__init__.py +3 -1
- nautobot/extras/querysets.py +30 -66
- nautobot/extras/signals.py +95 -100
- nautobot/extras/tables.py +165 -12
- nautobot/extras/templates/extras/dynamicgroup.html +44 -15
- nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
- nautobot/extras/templates/extras/job.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +61 -74
- nautobot/extras/templates/extras/metadatatype_create.html +89 -0
- nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
- nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
- nautobot/extras/templates/extras/objectchange_list.html +0 -12
- nautobot/extras/templates/extras/plugins_list.html +1 -3
- nautobot/extras/templates/extras/role_retrieve.html +48 -0
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
- nautobot/extras/tests/integration/test_customfields.py +1 -0
- nautobot/extras/tests/test_api.py +509 -23
- nautobot/extras/tests/test_changelog.py +20 -9
- nautobot/extras/tests/test_context_managers.py +22 -15
- nautobot/extras/tests/test_datasources.py +13 -1
- nautobot/extras/tests/test_dynamicgroups.py +201 -171
- nautobot/extras/tests/test_filters.py +211 -12
- nautobot/extras/tests/test_jobs.py +6 -6
- nautobot/extras/tests/test_models.py +501 -4
- nautobot/extras/tests/test_relationships.py +1 -0
- nautobot/extras/tests/test_views.py +565 -8
- nautobot/extras/tests/test_webhooks.py +1 -1
- nautobot/extras/urls.py +5 -0
- nautobot/extras/utils.py +51 -11
- nautobot/extras/views.py +542 -76
- nautobot/ipam/__init__.py +0 -1
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/factory.py +17 -19
- nautobot/ipam/filters.py +13 -0
- nautobot/ipam/forms.py +8 -4
- nautobot/ipam/graphql/types.py +2 -2
- nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
- nautobot/ipam/models.py +11 -8
- nautobot/ipam/querysets.py +1 -1
- nautobot/ipam/signals.py +4 -2
- nautobot/ipam/tables.py +5 -0
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -0
- nautobot/ipam/tests/test_api.py +37 -18
- nautobot/ipam/tests/test_filters.py +26 -2
- nautobot/ipam/tests/test_models.py +6 -0
- nautobot/ipam/tests/test_querysets.py +1 -1
- nautobot/ipam/tests/test_views.py +3 -2
- nautobot/ipam/urls.py +2 -2
- nautobot/ipam/views.py +18 -26
- nautobot/project-static/css/base.css +20 -0
- nautobot/project-static/css/dark.css +11 -0
- nautobot/project-static/docs/404.html +892 -88
- nautobot/project-static/docs/apps/index.html +892 -88
- nautobot/project-static/docs/apps/nautobot-apps.html +892 -88
- nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +919 -120
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1618 -903
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +935 -144
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +977 -188
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +901 -99
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +897 -93
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +991 -193
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +974 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1078 -272
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1242 -334
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1727 -875
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1164 -381
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2088 -1374
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2246 -1422
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +912 -111
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +963 -163
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1010 -223
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1913 -1277
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1846 -1102
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2331 -1699
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1802 -1024
- nautobot/project-static/docs/development/apps/api/configuration-view.html +892 -88
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/global-search.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/graphql.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/index.html +942 -90
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +892 -88
- nautobot/project-static/docs/development/apps/api/prometheus.html +892 -88
- nautobot/project-static/docs/development/apps/api/setup.html +892 -88
- nautobot/project-static/docs/development/apps/api/testing.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/base-template.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/notes.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/urls.html +892 -88
- nautobot/project-static/docs/development/apps/index.html +892 -88
- nautobot/project-static/docs/development/apps/migration/code-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/from-v1.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +892 -88
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +892 -88
- nautobot/project-static/docs/development/core/application-registry.html +892 -88
- nautobot/project-static/docs/development/core/best-practices.html +893 -88
- nautobot/project-static/docs/development/core/bootstrap-ui.html +892 -88
- nautobot/project-static/docs/development/core/caching.html +892 -88
- nautobot/project-static/docs/development/core/controllers.html +892 -88
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +892 -88
- nautobot/project-static/docs/development/core/generic-views.html +892 -88
- nautobot/project-static/docs/development/core/getting-started.html +892 -88
- nautobot/project-static/docs/development/core/homepage.html +892 -88
- nautobot/project-static/docs/development/core/index.html +892 -88
- nautobot/project-static/docs/development/core/model-checklist.html +901 -89
- nautobot/project-static/docs/development/core/model-features.html +892 -88
- nautobot/project-static/docs/development/core/natural-keys.html +892 -88
- nautobot/project-static/docs/development/core/navigation-menu.html +892 -88
- nautobot/project-static/docs/development/core/release-checklist.html +895 -91
- nautobot/project-static/docs/development/core/role-internals.html +892 -88
- nautobot/project-static/docs/development/core/settings.html +892 -88
- nautobot/project-static/docs/development/core/style-guide.html +893 -89
- nautobot/project-static/docs/development/core/templates.html +904 -89
- nautobot/project-static/docs/development/core/testing.html +892 -88
- nautobot/project-static/docs/development/core/user-preferences.html +892 -88
- nautobot/project-static/docs/development/index.html +892 -88
- nautobot/project-static/docs/development/jobs/index.html +893 -89
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +892 -88
- nautobot/project-static/docs/index.html +892 -88
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
- nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
- nautobot/project-static/docs/models/dcim/module.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
- nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
- nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
- nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
- nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
- nautobot/project-static/docs/models/extras/role.html +15 -0
- nautobot/project-static/docs/models/extras/savedview.html +15 -0
- nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
- nautobot/project-static/docs/models/extras/status.html +15 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +900 -89
- nautobot/project-static/docs/overview/design_philosophy.html +892 -88
- nautobot/project-static/docs/release-notes/index.html +1129 -92
- nautobot/project-static/docs/release-notes/version-1.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.2.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.3.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.4.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.5.html +893 -89
- nautobot/project-static/docs/release-notes/version-1.6.html +893 -89
- nautobot/project-static/docs/release-notes/version-2.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.2.html +895 -91
- nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
- nautobot/project-static/docs/requirements.txt +5 -5
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +331 -256
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +992 -174
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +896 -88
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +944 -153
- nautobot/project-static/docs/user-guide/administration/installation/index.html +901 -93
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +934 -122
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +954 -157
- nautobot/project-static/docs/user-guide/administration/installation/services.html +913 -112
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +908 -99
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +892 -88
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +893 -89
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +893 -89
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +896 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +895 -91
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +918 -100
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +913 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +914 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +936 -118
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +928 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +937 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +928 -110
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +923 -115
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +916 -112
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +940 -83
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +943 -86
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +921 -103
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +929 -125
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +922 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +936 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +901 -96
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +897 -89
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +892 -88
- nautobot/project-static/docs/user-guide/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1258 -785
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +896 -88
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +950 -121
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +892 -88
- nautobot/project-static/js/forms.js +71 -0
- nautobot/project-static/js/table_sorting_indicator.js +46 -0
- nautobot/project-static/js/tableconfig.js +6 -1
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/tenancy/__init__.py +0 -1
- nautobot/tenancy/apps.py +1 -0
- nautobot/tenancy/factory.py +3 -2
- nautobot/tenancy/filters/__init__.py +1 -0
- nautobot/tenancy/forms.py +1 -1
- nautobot/tenancy/templates/tenancy/tenant.html +24 -20
- nautobot/tenancy/views.py +11 -10
- nautobot/users/__init__.py +0 -1
- nautobot/users/api/serializers.py +1 -1
- nautobot/users/api/views.py +4 -2
- nautobot/users/apps.py +3 -2
- nautobot/users/factory.py +3 -3
- nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
- nautobot/users/models.py +12 -0
- nautobot/users/tests/test_filters.py +6 -3
- nautobot/users/urls.py +8 -0
- nautobot/virtualization/__init__.py +0 -1
- nautobot/virtualization/apps.py +1 -0
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/forms.py +11 -3
- nautobot/virtualization/graphql/types.py +2 -2
- nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
- nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
- nautobot/virtualization/models.py +0 -2
- nautobot/virtualization/tables.py +10 -3
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
- nautobot/virtualization/tests/test_api.py +9 -4
- nautobot/virtualization/tests/test_filters.py +22 -0
- nautobot/virtualization/tests/test_models.py +7 -3
- nautobot/virtualization/tests/test_views.py +19 -3
- nautobot/virtualization/urls.py +2 -2
- nautobot/virtualization/views.py +10 -32
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/METADATA +20 -18
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/RECORD +677 -557
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
- nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
- nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
- nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
- nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
- nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
- nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
- nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
- /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
nautobot/core/views/generic.py
CHANGED
|
@@ -5,6 +5,7 @@ import re
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib import messages
|
|
7
7
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
8
|
+
from django.contrib.auth.models import AnonymousUser
|
|
8
9
|
from django.contrib.contenttypes.models import ContentType
|
|
9
10
|
from django.core.exceptions import (
|
|
10
11
|
FieldDoesNotExist,
|
|
@@ -12,10 +13,11 @@ from django.core.exceptions import (
|
|
|
12
13
|
ValidationError,
|
|
13
14
|
)
|
|
14
15
|
from django.db import IntegrityError, transaction
|
|
15
|
-
from django.db.models import ManyToManyField, ProtectedError
|
|
16
|
+
from django.db.models import ManyToManyField, ProtectedError, Q
|
|
16
17
|
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
17
|
-
from django.http import HttpResponse
|
|
18
|
+
from django.http import HttpResponse
|
|
18
19
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
20
|
+
from django.urls import reverse
|
|
19
21
|
from django.utils.encoding import iri_to_uri
|
|
20
22
|
from django.utils.html import format_html
|
|
21
23
|
from django.utils.http import url_has_allowed_host_and_scheme
|
|
@@ -38,7 +40,6 @@ from nautobot.core.forms import (
|
|
|
38
40
|
from nautobot.core.forms.forms import DynamicFilterFormSet
|
|
39
41
|
from nautobot.core.templatetags.helpers import bettertitle, validated_viewname
|
|
40
42
|
from nautobot.core.utils.config import get_settings_or_config
|
|
41
|
-
from nautobot.core.utils.lookup import get_created_and_last_updated_usernames_for_model
|
|
42
43
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
43
44
|
from nautobot.core.utils.requests import (
|
|
44
45
|
convert_querydict_to_factory_formset_acceptable_querydict,
|
|
@@ -49,14 +50,15 @@ from nautobot.core.views.mixins import GetReturnURLMixin, ObjectPermissionRequir
|
|
|
49
50
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
50
51
|
from nautobot.core.views.utils import (
|
|
51
52
|
check_filter_for_display,
|
|
53
|
+
common_detail_view_context,
|
|
52
54
|
get_csv_form_fields_from_serializer_class,
|
|
53
55
|
handle_protectederror,
|
|
54
56
|
import_csv_helper,
|
|
55
57
|
prepare_cloned_fields,
|
|
58
|
+
view_changes_not_saved,
|
|
56
59
|
)
|
|
57
60
|
from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
|
|
58
|
-
from nautobot.extras.models import
|
|
59
|
-
from nautobot.extras.tables import AssociatedContactsTable
|
|
61
|
+
from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
|
|
60
62
|
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
|
|
61
63
|
|
|
62
64
|
|
|
@@ -111,53 +113,17 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
|
111
113
|
Generic GET handler for accessing an object.
|
|
112
114
|
"""
|
|
113
115
|
instance = get_object_or_404(self.queryset, **kwargs)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
temp_fake_context = {
|
|
125
|
-
"object": instance,
|
|
126
|
-
"request": request,
|
|
127
|
-
"settings": {},
|
|
128
|
-
"csrf_token": "",
|
|
129
|
-
"perms": {},
|
|
130
|
-
}
|
|
116
|
+
content_type = ContentType.objects.get_for_model(self.queryset.model)
|
|
117
|
+
context = {
|
|
118
|
+
"object": instance,
|
|
119
|
+
"content_type": content_type,
|
|
120
|
+
"verbose_name": self.queryset.model._meta.verbose_name,
|
|
121
|
+
"verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
|
|
122
|
+
**common_detail_view_context(request, instance),
|
|
123
|
+
**self.get_extra_context(request, instance),
|
|
124
|
+
}
|
|
131
125
|
|
|
132
|
-
|
|
133
|
-
resp = {"tabs": plugin_tabs}
|
|
134
|
-
return JsonResponse(resp)
|
|
135
|
-
else:
|
|
136
|
-
content_type = ContentType.objects.get_for_model(self.queryset.model)
|
|
137
|
-
context = {
|
|
138
|
-
"object": instance,
|
|
139
|
-
"content_type": content_type,
|
|
140
|
-
"verbose_name": self.queryset.model._meta.verbose_name,
|
|
141
|
-
"verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
|
|
142
|
-
"created_by": created_by,
|
|
143
|
-
"last_updated_by": last_updated_by,
|
|
144
|
-
**self.get_extra_context(request, instance),
|
|
145
|
-
}
|
|
146
|
-
if instance.is_contact_associable_model:
|
|
147
|
-
paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
148
|
-
associations = (
|
|
149
|
-
ContactAssociation.objects.filter(
|
|
150
|
-
associated_object_id=instance.id,
|
|
151
|
-
associated_object_type=content_type,
|
|
152
|
-
)
|
|
153
|
-
.restrict(request.user, "view")
|
|
154
|
-
.order_by("role__name")
|
|
155
|
-
)
|
|
156
|
-
associations_table = AssociatedContactsTable(associations, orderable=False)
|
|
157
|
-
RequestConfig(request, paginate).configure(associations_table)
|
|
158
|
-
associations_table.columns.show("pk")
|
|
159
|
-
context["associated_contacts_table"] = associations_table
|
|
160
|
-
return render(request, self.get_template_name(), context)
|
|
126
|
+
return render(request, self.get_template_name(), context)
|
|
161
127
|
|
|
162
128
|
|
|
163
129
|
class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
@@ -184,12 +150,19 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
184
150
|
"page", # used by django-tables2.RequestConfig
|
|
185
151
|
"per_page", # used by get_paginate_count
|
|
186
152
|
"sort", # table sorting
|
|
153
|
+
"saved_view", # saved_view indicator pk or composite keys
|
|
154
|
+
"table_changes_pending", # indicator for if there is any table changes not applied to the saved view
|
|
155
|
+
"all_filters_removed", # indicator for if all filters have been removed from the saved view
|
|
156
|
+
"clear_view", # indicator for if the clear view button is clicked or not
|
|
187
157
|
)
|
|
188
158
|
|
|
189
159
|
def get_filter_params(self, request):
|
|
190
160
|
"""Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
|
|
191
|
-
|
|
192
|
-
|
|
161
|
+
params = request.GET.copy()
|
|
162
|
+
filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset())
|
|
163
|
+
if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
|
|
164
|
+
return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
|
|
165
|
+
return filter_params
|
|
193
166
|
|
|
194
167
|
def get_required_permission(self):
|
|
195
168
|
return get_permission_for_model(self.queryset.model, "view")
|
|
@@ -225,10 +198,41 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
225
198
|
model = self.queryset.model
|
|
226
199
|
content_type = ContentType.objects.get_for_model(model)
|
|
227
200
|
|
|
201
|
+
filter_params = request.GET
|
|
202
|
+
user = request.user
|
|
228
203
|
display_filter_params = []
|
|
229
204
|
dynamic_filter_form = None
|
|
230
205
|
filter_form = None
|
|
231
206
|
hide_hierarchy_ui = False
|
|
207
|
+
clear_view = request.GET.get("clear_view", False)
|
|
208
|
+
|
|
209
|
+
# If the user clicks on the clear view button, we do not check for global or user defaults
|
|
210
|
+
if not clear_view and not request.GET.get("saved_view"):
|
|
211
|
+
# Check if there is a default for this view for this specific user
|
|
212
|
+
app_label, model_name = model._meta.label.split(".")
|
|
213
|
+
view_name = f"{app_label}:{model_name.lower()}_list"
|
|
214
|
+
|
|
215
|
+
if not isinstance(user, AnonymousUser):
|
|
216
|
+
try:
|
|
217
|
+
user_default_saved_view_pk = UserSavedViewAssociation.objects.get(
|
|
218
|
+
user=user, view_name=view_name
|
|
219
|
+
).saved_view.pk
|
|
220
|
+
# Saved view should either belong to the user or be public
|
|
221
|
+
SavedView.objects.get(
|
|
222
|
+
Q(pk=user_default_saved_view_pk),
|
|
223
|
+
Q(owner=user) | Q(is_shared=True),
|
|
224
|
+
)
|
|
225
|
+
sv_url = reverse("extras:savedview", kwargs={"pk": user_default_saved_view_pk})
|
|
226
|
+
return redirect(sv_url)
|
|
227
|
+
except ObjectDoesNotExist:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
# Check if there is a global default for this view
|
|
231
|
+
try:
|
|
232
|
+
global_saved_view = SavedView.objects.get(view=view_name, is_global_default=True)
|
|
233
|
+
return redirect(reverse("extras:savedview", kwargs={"pk": global_saved_view.pk}))
|
|
234
|
+
except ObjectDoesNotExist:
|
|
235
|
+
pass
|
|
232
236
|
|
|
233
237
|
if self.filterset:
|
|
234
238
|
filter_params = self.get_filter_params(request)
|
|
@@ -254,7 +258,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
254
258
|
|
|
255
259
|
if request.GET:
|
|
256
260
|
factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
|
|
257
|
-
|
|
261
|
+
filter_params, filterset
|
|
258
262
|
)
|
|
259
263
|
dynamic_filter_form = DynamicFilterFormSet(filterset=filterset, data=factory_formset_params)
|
|
260
264
|
else:
|
|
@@ -296,18 +300,50 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
296
300
|
|
|
297
301
|
table = None
|
|
298
302
|
table_config_form = None
|
|
303
|
+
current_saved_view = None
|
|
304
|
+
current_saved_view_pk = self.request.GET.get("saved_view", None)
|
|
305
|
+
list_url = validated_viewname(model, "list")
|
|
306
|
+
# We are not using .restrict(request.user, "view") here
|
|
307
|
+
# User should be able to see any saved view that he has the list view access to.
|
|
308
|
+
if user.has_perms(["extras.view_savedview"]):
|
|
309
|
+
saved_views = SavedView.objects.filter(view=list_url).order_by("name").only("pk", "name")
|
|
310
|
+
else:
|
|
311
|
+
shared_saved_views = (
|
|
312
|
+
SavedView.objects.filter(view=list_url, is_shared=True).order_by("name").only("pk", "name")
|
|
313
|
+
)
|
|
314
|
+
user_owned_saved_views = (
|
|
315
|
+
SavedView.objects.filter(view=list_url, owner=user).order_by("name").only("pk", "name")
|
|
316
|
+
)
|
|
317
|
+
saved_views = shared_saved_views | user_owned_saved_views
|
|
299
318
|
if self.table:
|
|
300
319
|
# Construct the objects table
|
|
301
|
-
if
|
|
320
|
+
if current_saved_view_pk:
|
|
321
|
+
try:
|
|
322
|
+
# We are not using .restrict(request.user, "view") here
|
|
323
|
+
# User should be able to see any saved view that he has the list view access to.
|
|
324
|
+
current_saved_view = SavedView.objects.get(view=list_url, pk=current_saved_view_pk)
|
|
325
|
+
except ObjectDoesNotExist:
|
|
326
|
+
messages.error(request, f"Saved view {current_saved_view_pk} not found")
|
|
327
|
+
if self.request.GET.getlist("sort") or (
|
|
328
|
+
current_saved_view is not None and current_saved_view.config.get("sort_order")
|
|
329
|
+
):
|
|
302
330
|
hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
|
|
303
|
-
|
|
331
|
+
table_changes_pending = self.request.GET.get("table_changes_pending", False)
|
|
332
|
+
|
|
333
|
+
table = self.table(
|
|
334
|
+
self.queryset,
|
|
335
|
+
table_changes_pending=table_changes_pending,
|
|
336
|
+
saved_view=current_saved_view,
|
|
337
|
+
user=request.user,
|
|
338
|
+
hide_hierarchy_ui=hide_hierarchy_ui,
|
|
339
|
+
)
|
|
304
340
|
if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
|
|
305
341
|
table.columns.show("pk")
|
|
306
342
|
|
|
307
343
|
# Apply the request context
|
|
308
344
|
paginate = {
|
|
309
345
|
"paginator_class": EnhancedPaginator,
|
|
310
|
-
"per_page": get_paginate_count(request),
|
|
346
|
+
"per_page": get_paginate_count(request, current_saved_view),
|
|
311
347
|
}
|
|
312
348
|
RequestConfig(request, paginate).configure(table)
|
|
313
349
|
table_config_form = TableConfigForm(table=table)
|
|
@@ -320,10 +356,13 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
320
356
|
|
|
321
357
|
# For the search form field, use a custom placeholder.
|
|
322
358
|
q_placeholder = "Search " + bettertitle(model._meta.verbose_name_plural)
|
|
323
|
-
search_form = SearchForm(data=
|
|
359
|
+
search_form = SearchForm(data=filter_params, q_placeholder=q_placeholder)
|
|
324
360
|
|
|
325
361
|
valid_actions = self.validate_action_buttons(request)
|
|
326
362
|
|
|
363
|
+
# Query SavedViews for dropdown button
|
|
364
|
+
new_changes_not_applied = view_changes_not_saved(request, self, current_saved_view)
|
|
365
|
+
|
|
327
366
|
context = {
|
|
328
367
|
"content_type": content_type,
|
|
329
368
|
"table": table,
|
|
@@ -334,8 +373,12 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
334
373
|
"filter_form": filter_form,
|
|
335
374
|
"dynamic_filter_form": dynamic_filter_form,
|
|
336
375
|
"search_form": search_form,
|
|
337
|
-
"list_url":
|
|
376
|
+
"list_url": list_url,
|
|
338
377
|
"title": bettertitle(model._meta.verbose_name_plural),
|
|
378
|
+
"new_changes_not_applied": new_changes_not_applied,
|
|
379
|
+
"current_saved_view": current_saved_view,
|
|
380
|
+
"saved_views": saved_views,
|
|
381
|
+
"model": model,
|
|
339
382
|
}
|
|
340
383
|
|
|
341
384
|
# `extra_context()` would require `request` access, however `request` parameter cannot simply be
|
|
@@ -439,7 +482,12 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
439
482
|
def post(self, request, *args, **kwargs):
|
|
440
483
|
logger = logging.getLogger(__name__ + ".ObjectEditView")
|
|
441
484
|
obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
|
|
442
|
-
form = self.model_form(
|
|
485
|
+
form = self.model_form(
|
|
486
|
+
data=request.POST,
|
|
487
|
+
files=request.FILES,
|
|
488
|
+
initial=normalize_querydict(request.GET, form_class=self.model_form),
|
|
489
|
+
instance=obj,
|
|
490
|
+
)
|
|
443
491
|
restrict_form_fields(form, request.user)
|
|
444
492
|
|
|
445
493
|
if form.is_valid():
|
|
@@ -538,6 +586,7 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
538
586
|
|
|
539
587
|
if form.is_valid():
|
|
540
588
|
logger.debug("Form validation was successful")
|
|
589
|
+
msg = f"Deleted {self.queryset.model._meta.verbose_name} {obj}"
|
|
541
590
|
|
|
542
591
|
try:
|
|
543
592
|
obj.delete()
|
|
@@ -546,7 +595,6 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
546
595
|
handle_protectederror([obj], request, e)
|
|
547
596
|
return redirect(obj.get_absolute_url())
|
|
548
597
|
|
|
549
|
-
msg = f"Deleted {self.queryset.model._meta.verbose_name} {obj}"
|
|
550
598
|
logger.info(msg)
|
|
551
599
|
messages.success(request, msg)
|
|
552
600
|
|
|
@@ -752,7 +800,9 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
752
800
|
|
|
753
801
|
related_obj_pks = []
|
|
754
802
|
for i, rel_obj_data in enumerate(data.get(field_name, [])):
|
|
755
|
-
|
|
803
|
+
# add parent object key to related object data
|
|
804
|
+
rel_obj_data[obj._meta.verbose_name.replace(" ", "_")] = str(obj.pk)
|
|
805
|
+
f = related_object_form(rel_obj_data)
|
|
756
806
|
|
|
757
807
|
for subfield_name, field in f.fields.items():
|
|
758
808
|
if subfield_name not in rel_obj_data and hasattr(field, "initial"):
|
|
@@ -1210,7 +1260,7 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1210
1260
|
Delete objects in bulk.
|
|
1211
1261
|
|
|
1212
1262
|
queryset: Custom queryset to use when retrieving objects (e.g. to select related objects)
|
|
1213
|
-
|
|
1263
|
+
filterset: FilterSet to apply when deleting by QuerySet
|
|
1214
1264
|
table: The table used to display devices being deleted
|
|
1215
1265
|
form: The form class used to delete objects in bulk
|
|
1216
1266
|
template_name: The name of the template
|
|
@@ -1336,7 +1386,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
|
|
|
1336
1386
|
return get_permission_for_model(self.queryset.model, "add")
|
|
1337
1387
|
|
|
1338
1388
|
def get(self, request):
|
|
1339
|
-
form = self.form(initial=request.GET)
|
|
1389
|
+
form = self.form(initial=normalize_querydict(request.GET, form_class=self.form))
|
|
1340
1390
|
model_form = self.model_form(request.GET)
|
|
1341
1391
|
|
|
1342
1392
|
return render(
|
|
@@ -1352,8 +1402,8 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
|
|
|
1352
1402
|
|
|
1353
1403
|
def post(self, request):
|
|
1354
1404
|
logger = logging.getLogger(__name__ + ".ComponentCreateView")
|
|
1355
|
-
form = self.form(request.POST, initial=request.GET)
|
|
1356
|
-
model_form = self.model_form(request.POST)
|
|
1405
|
+
form = self.form(request.POST, initial=normalize_querydict(request.GET, form_class=self.form))
|
|
1406
|
+
model_form = self.model_form(request.POST, initial=normalize_querydict(request.GET, form_class=self.model_form))
|
|
1357
1407
|
|
|
1358
1408
|
if form.is_valid():
|
|
1359
1409
|
new_components = []
|
|
@@ -1368,7 +1418,9 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
|
|
|
1368
1418
|
data["label"] = label
|
|
1369
1419
|
if hasattr(form, "get_iterative_data"):
|
|
1370
1420
|
data.update(form.get_iterative_data(i))
|
|
1371
|
-
component_form = self.model_form(
|
|
1421
|
+
component_form = self.model_form(
|
|
1422
|
+
data, initial=normalize_querydict(request.GET, form_class=self.model_form)
|
|
1423
|
+
)
|
|
1372
1424
|
|
|
1373
1425
|
if component_form.is_valid():
|
|
1374
1426
|
new_components.append(component_form)
|
nautobot/core/views/mixins.py
CHANGED
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from django.contrib import messages
|
|
4
4
|
from django.contrib.auth.mixins import AccessMixin
|
|
5
|
+
from django.contrib.auth.models import AnonymousUser
|
|
5
6
|
from django.contrib.contenttypes.models import ContentType
|
|
6
7
|
from django.core.exceptions import (
|
|
7
8
|
FieldDoesNotExist,
|
|
@@ -10,7 +11,7 @@ from django.core.exceptions import (
|
|
|
10
11
|
ValidationError,
|
|
11
12
|
)
|
|
12
13
|
from django.db import transaction
|
|
13
|
-
from django.db.models import ManyToManyField, ProtectedError
|
|
14
|
+
from django.db.models import ManyToManyField, ProtectedError, Q
|
|
14
15
|
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
15
16
|
from django.http import HttpResponse
|
|
16
17
|
from django.shortcuts import get_object_or_404, redirect
|
|
@@ -37,7 +38,7 @@ from nautobot.core.forms import (
|
|
|
37
38
|
restrict_form_fields,
|
|
38
39
|
)
|
|
39
40
|
from nautobot.core.utils import lookup, permissions
|
|
40
|
-
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
41
|
+
from nautobot.core.utils.requests import get_filterable_params_from_filter_params, normalize_querydict
|
|
41
42
|
from nautobot.core.views.renderers import NautobotHTMLRenderer
|
|
42
43
|
from nautobot.core.views.utils import (
|
|
43
44
|
get_csv_form_fields_from_serializer_class,
|
|
@@ -47,9 +48,9 @@ from nautobot.core.views.utils import (
|
|
|
47
48
|
)
|
|
48
49
|
from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
|
|
49
50
|
from nautobot.extras.forms import NoteForm
|
|
50
|
-
from nautobot.extras.models import ExportTemplate
|
|
51
|
+
from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
|
|
51
52
|
from nautobot.extras.tables import NoteTable, ObjectChangeTable
|
|
52
|
-
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
|
|
53
|
+
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, get_base_template, remove_prefix_from_cf_key
|
|
53
54
|
|
|
54
55
|
PERMISSIONS_ACTION_MAP = {
|
|
55
56
|
"list": "view",
|
|
@@ -59,6 +60,7 @@ PERMISSIONS_ACTION_MAP = {
|
|
|
59
60
|
"update": "change",
|
|
60
61
|
"bulk_create": "add", # 3.0 TODO: remove, replaced by system Job
|
|
61
62
|
"bulk_destroy": "delete",
|
|
63
|
+
"bulk_rename": "change",
|
|
62
64
|
"bulk_update": "change",
|
|
63
65
|
"changelog": "view",
|
|
64
66
|
"notes": "view",
|
|
@@ -459,8 +461,11 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
|
|
|
459
461
|
|
|
460
462
|
def get_filter_params(self, request):
|
|
461
463
|
"""Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
|
|
462
|
-
|
|
463
|
-
|
|
464
|
+
params = request.GET.copy()
|
|
465
|
+
filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset_class())
|
|
466
|
+
if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
|
|
467
|
+
return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
|
|
468
|
+
return filter_params
|
|
464
469
|
|
|
465
470
|
def get_queryset(self):
|
|
466
471
|
"""
|
|
@@ -586,6 +591,9 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
|
|
|
586
591
|
queryset = self.get_queryset()
|
|
587
592
|
return queryset.all()
|
|
588
593
|
|
|
594
|
+
def extra_post_save_action(self, obj, form):
|
|
595
|
+
"""Extra actions after a form is saved"""
|
|
596
|
+
|
|
589
597
|
|
|
590
598
|
class ObjectDetailViewMixin(NautobotViewSetMixin, mixins.RetrieveModelMixin):
|
|
591
599
|
"""
|
|
@@ -596,11 +604,7 @@ class ObjectDetailViewMixin(NautobotViewSetMixin, mixins.RetrieveModelMixin):
|
|
|
596
604
|
"""
|
|
597
605
|
Retrieve a model instance.
|
|
598
606
|
"""
|
|
599
|
-
|
|
600
|
-
serializer = self.get_serializer(instance)
|
|
601
|
-
|
|
602
|
-
context = serializer.data
|
|
603
|
-
context["use_new_ui"] = True
|
|
607
|
+
context = {"use_new_ui": True}
|
|
604
608
|
return Response(context)
|
|
605
609
|
|
|
606
610
|
|
|
@@ -618,6 +622,10 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
|
|
|
618
622
|
"page", # used by django-tables2.RequestConfig
|
|
619
623
|
"per_page", # used by get_paginate_count
|
|
620
624
|
"sort", # table sorting
|
|
625
|
+
"saved_view", # saved_view indicator pk or composite keys
|
|
626
|
+
"table_changes_pending", # indicator for if there is any table changes not applied to the saved view
|
|
627
|
+
"all_filters_removed", # indicator for if all filters have been removed from the saved view
|
|
628
|
+
"clear_view", # indicator for if the clear view button is clicked or not
|
|
621
629
|
)
|
|
622
630
|
|
|
623
631
|
def filter_queryset(self, queryset):
|
|
@@ -684,13 +692,43 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
|
|
|
684
692
|
List the model instances.
|
|
685
693
|
"""
|
|
686
694
|
context = {"use_new_ui": True}
|
|
695
|
+
queryset = self.get_queryset()
|
|
696
|
+
clear_view = request.GET.get("clear_view", False)
|
|
687
697
|
if "export" in request.GET: # 3.0 TODO: remove, irrelevant after #4746
|
|
688
|
-
queryset = self.get_queryset()
|
|
689
698
|
model = queryset.model
|
|
690
699
|
content_type = ContentType.objects.get_for_model(model)
|
|
691
700
|
response = self.check_for_export(request, model, content_type)
|
|
692
701
|
if response is not None:
|
|
693
702
|
return response
|
|
703
|
+
|
|
704
|
+
# If the user clicks on the clear view button, we do not check for global or user defaults
|
|
705
|
+
if not clear_view and not request.GET.get("saved_view"):
|
|
706
|
+
# Check if there is a default for this view for this specific user
|
|
707
|
+
app_label, model_name = queryset.model._meta.label.split(".")
|
|
708
|
+
view_name = f"{app_label}:{model_name.lower()}_list"
|
|
709
|
+
user = request.user
|
|
710
|
+
if not isinstance(user, AnonymousUser):
|
|
711
|
+
try:
|
|
712
|
+
user_default_saved_view_pk = UserSavedViewAssociation.objects.get(
|
|
713
|
+
user=user, view_name=view_name
|
|
714
|
+
).saved_view.pk
|
|
715
|
+
# Saved view should either belong to the user or be public
|
|
716
|
+
SavedView.objects.get(
|
|
717
|
+
Q(pk=user_default_saved_view_pk),
|
|
718
|
+
Q(owner=user) | Q(is_shared=True),
|
|
719
|
+
)
|
|
720
|
+
sv_url = reverse("extras:savedview", kwargs={"pk": user_default_saved_view_pk})
|
|
721
|
+
return redirect(sv_url)
|
|
722
|
+
except ObjectDoesNotExist:
|
|
723
|
+
pass
|
|
724
|
+
|
|
725
|
+
# Check if there is a global default for this view
|
|
726
|
+
try:
|
|
727
|
+
global_saved_view = SavedView.objects.get(view=view_name, is_global_default=True)
|
|
728
|
+
return redirect(reverse("extras:savedview", kwargs={"pk": global_saved_view.pk}))
|
|
729
|
+
except ObjectDoesNotExist:
|
|
730
|
+
pass
|
|
731
|
+
|
|
694
732
|
return Response(context)
|
|
695
733
|
|
|
696
734
|
|
|
@@ -707,8 +745,8 @@ class ObjectDestroyViewMixin(NautobotViewSetMixin, mixins.DestroyModelMixin):
|
|
|
707
745
|
queryset = self.get_queryset()
|
|
708
746
|
try:
|
|
709
747
|
with transaction.atomic():
|
|
710
|
-
obj.delete()
|
|
711
748
|
msg = f"Deleted {queryset.model._meta.verbose_name} {obj}"
|
|
749
|
+
obj.delete()
|
|
712
750
|
self.logger.info(msg)
|
|
713
751
|
messages.success(request, msg)
|
|
714
752
|
self.success_url = self.get_return_url(request, obj)
|
|
@@ -734,7 +772,7 @@ class ObjectDestroyViewMixin(NautobotViewSetMixin, mixins.DestroyModelMixin):
|
|
|
734
772
|
"""
|
|
735
773
|
self.obj = self.get_object()
|
|
736
774
|
form_class = self.get_form_class()
|
|
737
|
-
form = form_class(request.POST)
|
|
775
|
+
form = form_class(request.POST, initial=normalize_querydict(request.GET, form_class=form_class))
|
|
738
776
|
if form.is_valid():
|
|
739
777
|
return self.form_valid(form)
|
|
740
778
|
else:
|
|
@@ -746,6 +784,18 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
|
|
|
746
784
|
UI mixin to create or update a model instance.
|
|
747
785
|
"""
|
|
748
786
|
|
|
787
|
+
def extra_message_context(self, obj):
|
|
788
|
+
"""
|
|
789
|
+
Context variables for this extra message.
|
|
790
|
+
"""
|
|
791
|
+
return {}
|
|
792
|
+
|
|
793
|
+
def extra_message(self, **kwargs):
|
|
794
|
+
"""
|
|
795
|
+
Append extra message at the end of create or update success message.
|
|
796
|
+
"""
|
|
797
|
+
return ""
|
|
798
|
+
|
|
749
799
|
def _process_create_or_update_form(self, form):
|
|
750
800
|
"""
|
|
751
801
|
Helper method to create or update an object after the form is validated successfully.
|
|
@@ -765,9 +815,14 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
|
|
|
765
815
|
msg = f'{"Created" if object_created else "Modified"} {queryset.model._meta.verbose_name}'
|
|
766
816
|
self.logger.info(f"{msg} {obj} (PK: {obj.pk})")
|
|
767
817
|
try:
|
|
768
|
-
msg = format_html(
|
|
818
|
+
msg = format_html(
|
|
819
|
+
'{} <a href="{}">{}</a>' + self.extra_message(**self.extra_message_context(obj)),
|
|
820
|
+
msg,
|
|
821
|
+
obj.get_absolute_url(),
|
|
822
|
+
obj,
|
|
823
|
+
)
|
|
769
824
|
except AttributeError:
|
|
770
|
-
msg = format_html("{} {}", msg, obj)
|
|
825
|
+
msg = format_html("{} {}" + self.extra_message(**self.extra_message_context(obj)), msg, obj)
|
|
771
826
|
messages.success(request, msg)
|
|
772
827
|
if "_addanother" in request.POST:
|
|
773
828
|
# If the object has clone_fields, pre-populate a new instance of the form
|
|
@@ -800,7 +855,12 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
|
|
|
800
855
|
"""
|
|
801
856
|
self.obj = self.get_object()
|
|
802
857
|
form_class = self.get_form_class()
|
|
803
|
-
form = form_class(
|
|
858
|
+
form = form_class(
|
|
859
|
+
data=request.POST,
|
|
860
|
+
files=request.FILES,
|
|
861
|
+
initial=normalize_querydict(request.GET, form_class=form_class),
|
|
862
|
+
instance=self.obj,
|
|
863
|
+
)
|
|
804
864
|
restrict_form_fields(form, request.user)
|
|
805
865
|
if form.is_valid():
|
|
806
866
|
return self.form_valid(form)
|
|
@@ -825,7 +885,12 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
|
|
|
825
885
|
"""
|
|
826
886
|
self.obj = self.get_object()
|
|
827
887
|
form_class = self.get_form_class()
|
|
828
|
-
form = form_class(
|
|
888
|
+
form = form_class(
|
|
889
|
+
data=request.POST,
|
|
890
|
+
files=request.FILES,
|
|
891
|
+
initial=normalize_querydict(request.GET, form_class=form_class),
|
|
892
|
+
instance=self.obj,
|
|
893
|
+
)
|
|
829
894
|
restrict_form_fields(form, request.user)
|
|
830
895
|
if form.is_valid():
|
|
831
896
|
return self.form_valid(form)
|
|
@@ -878,21 +943,18 @@ class ObjectBulkDestroyViewMixin(NautobotViewSetMixin, BulkDestroyModelMixin):
|
|
|
878
943
|
model = queryset.model
|
|
879
944
|
# Are we deleting *all* objects in the queryset or just a selected subset?
|
|
880
945
|
if request.POST.get("_all"):
|
|
881
|
-
|
|
882
|
-
if not filter_params:
|
|
883
|
-
self.pk_list = list(model.objects.only("pk").all().values_list("pk", flat=True))
|
|
884
|
-
elif self.filterset_class is None:
|
|
885
|
-
raise NotImplementedError("filterset_class must be defined to use _all")
|
|
886
|
-
else:
|
|
946
|
+
if self.filterset_class is not None:
|
|
887
947
|
self.pk_list = list(
|
|
888
|
-
self.filterset_class(
|
|
948
|
+
self.filterset_class(request.GET, model.objects.only("pk")).qs.values_list("pk", flat=True)
|
|
889
949
|
)
|
|
950
|
+
else:
|
|
951
|
+
self.pk_list = list(model.objects.all().values_list("pk", flat=True))
|
|
890
952
|
else:
|
|
891
953
|
self.pk_list = list(request.POST.getlist("pk"))
|
|
892
954
|
form_class = self.get_form_class(**kwargs)
|
|
893
955
|
data = {}
|
|
894
956
|
if "_confirm" in request.POST:
|
|
895
|
-
form = form_class(request.POST)
|
|
957
|
+
form = form_class(request.POST, initial=normalize_querydict(request.GET, form_class=form_class))
|
|
896
958
|
if form.is_valid():
|
|
897
959
|
return self.form_valid(form)
|
|
898
960
|
else:
|
|
@@ -953,7 +1015,11 @@ class ObjectBulkCreateViewMixin(NautobotViewSetMixin): # 3.0 TODO: remove, unus
|
|
|
953
1015
|
|
|
954
1016
|
def perform_bulk_create(self, request):
|
|
955
1017
|
form_class = self.get_form_class()
|
|
956
|
-
form = form_class(
|
|
1018
|
+
form = form_class(
|
|
1019
|
+
data=request.POST,
|
|
1020
|
+
files=request.FILES,
|
|
1021
|
+
initial=normalize_querydict(request.GET, form_class=form_class),
|
|
1022
|
+
)
|
|
957
1023
|
if form.is_valid():
|
|
958
1024
|
return self.form_valid(form)
|
|
959
1025
|
else:
|
|
@@ -1031,6 +1097,8 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin):
|
|
|
1031
1097
|
if hasattr(form, "save_note") and callable(form.save_note):
|
|
1032
1098
|
form.save_note(instance=obj, user=request.user)
|
|
1033
1099
|
|
|
1100
|
+
self.extra_post_save_action(obj, form)
|
|
1101
|
+
|
|
1034
1102
|
# Enforce object-level permissions
|
|
1035
1103
|
if queryset.filter(pk__in=[obj.pk for obj in updated_objects]).count() != len(updated_objects):
|
|
1036
1104
|
raise ObjectDoesNotExist
|
|
@@ -1059,15 +1127,12 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin):
|
|
|
1059
1127
|
|
|
1060
1128
|
# If we are editing *all* objects in the queryset, replace the PK list with all matched objects.
|
|
1061
1129
|
if request.POST.get("_all"):
|
|
1062
|
-
|
|
1063
|
-
if not filter_params:
|
|
1064
|
-
self.pk_list = list(model.objects.only("pk").all().values_list("pk", flat=True))
|
|
1065
|
-
elif self.filterset_class is None:
|
|
1066
|
-
raise NotImplementedError("filterset_class must be defined to use _all")
|
|
1067
|
-
else:
|
|
1130
|
+
if self.filterset_class is not None:
|
|
1068
1131
|
self.pk_list = list(
|
|
1069
|
-
self.filterset_class(
|
|
1132
|
+
self.filterset_class(request.GET, model.objects.only("pk")).qs.values_list("pk", flat=True)
|
|
1070
1133
|
)
|
|
1134
|
+
else:
|
|
1135
|
+
self.pk_list = list(model.objects.all().values_list("pk", flat=True))
|
|
1071
1136
|
else:
|
|
1072
1137
|
self.pk_list = list(request.POST.getlist("pk"))
|
|
1073
1138
|
data = {}
|
|
@@ -1101,8 +1166,10 @@ class ObjectChangeLogViewMixin(NautobotViewSetMixin):
|
|
|
1101
1166
|
|
|
1102
1167
|
@drf_action(detail=True)
|
|
1103
1168
|
def changelog(self, request, *args, **kwargs):
|
|
1169
|
+
model = self.get_queryset().model
|
|
1104
1170
|
data = {
|
|
1105
|
-
"base_template": self.base_template,
|
|
1171
|
+
"base_template": get_base_template(self.base_template, model),
|
|
1172
|
+
"active_tab": "changelog",
|
|
1106
1173
|
}
|
|
1107
1174
|
return Response(data)
|
|
1108
1175
|
|
|
@@ -1116,7 +1183,9 @@ class ObjectNotesViewMixin(NautobotViewSetMixin):
|
|
|
1116
1183
|
|
|
1117
1184
|
@drf_action(detail=True)
|
|
1118
1185
|
def notes(self, request, *args, **kwargs):
|
|
1186
|
+
model = self.get_queryset().model
|
|
1119
1187
|
data = {
|
|
1120
|
-
"base_template": self.base_template,
|
|
1188
|
+
"base_template": get_base_template(self.base_template, model),
|
|
1189
|
+
"active_tab": "notes",
|
|
1121
1190
|
}
|
|
1122
1191
|
return Response(data)
|