nautobot 2.2.8__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 +110 -14
- nautobot/core/forms/fields.py +10 -4
- nautobot/core/forms/forms.py +11 -3
- nautobot/core/forms/widgets.py +18 -1
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/graphql/schema.py +28 -7
- nautobot/core/jobs/__init__.py +20 -3
- 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 +15 -19
- nautobot/core/settings.yaml +48 -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 +28 -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_general_functionality.py +1 -1
- 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 +204 -2
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +12 -5
- nautobot/core/tests/test_templatetags_netutils.py +3 -3
- 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 +282 -99
- nautobot/dcim/models/devices.py +472 -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 +248 -47
- 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/deviceredundancygroup_retrieve.html +6 -0
- 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 +1103 -8
- nautobot/dcim/tests/test_views.py +1525 -343
- nautobot/dcim/urls.py +17 -2
- nautobot/dcim/utils.py +2 -3
- nautobot/dcim/views.py +1109 -113
- 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 +73 -59
- 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 +26 -3
- nautobot/extras/jobs.py +2 -2
- 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 +87 -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 +109 -101
- nautobot/extras/tables.py +201 -17
- 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/job_detail.html +11 -0
- 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 +586 -8
- nautobot/extras/tests/test_webhooks.py +1 -1
- nautobot/extras/urls.py +5 -0
- nautobot/extras/utils.py +85 -16
- nautobot/extras/views.py +562 -122
- 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 +20 -20
- 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 +9 -2
- 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 +20 -34
- nautobot/project-static/css/base.css +21 -0
- nautobot/project-static/css/dark.css +11 -0
- nautobot/project-static/docs/404.html +894 -90
- nautobot/project-static/docs/apps/index.html +894 -90
- nautobot/project-static/docs/apps/nautobot-apps.html +894 -90
- 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 +921 -122
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +906 -103
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1620 -905
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +937 -146
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +979 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +903 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +899 -95
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +993 -195
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +976 -133
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1080 -274
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1244 -336
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1729 -877
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1166 -383
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2090 -1376
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2248 -1424
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +914 -113
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +965 -165
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1012 -225
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1915 -1279
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1848 -1104
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +906 -103
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2335 -1701
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1804 -1026
- nautobot/project-static/docs/development/apps/api/configuration-view.html +894 -90
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/global-search.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/graphql.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/index.html +944 -92
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +894 -90
- nautobot/project-static/docs/development/apps/api/prometheus.html +894 -90
- nautobot/project-static/docs/development/apps/api/setup.html +894 -90
- nautobot/project-static/docs/development/apps/api/testing.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/base-template.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/notes.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/urls.html +894 -90
- nautobot/project-static/docs/development/apps/index.html +894 -90
- nautobot/project-static/docs/development/apps/migration/code-updates.html +894 -90
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +894 -90
- nautobot/project-static/docs/development/apps/migration/from-v1.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +894 -90
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +894 -90
- nautobot/project-static/docs/development/core/application-registry.html +894 -90
- nautobot/project-static/docs/development/core/best-practices.html +895 -90
- nautobot/project-static/docs/development/core/bootstrap-ui.html +894 -90
- nautobot/project-static/docs/development/core/caching.html +894 -90
- nautobot/project-static/docs/development/core/controllers.html +894 -90
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +894 -90
- nautobot/project-static/docs/development/core/generic-views.html +894 -90
- nautobot/project-static/docs/development/core/getting-started.html +894 -90
- nautobot/project-static/docs/development/core/homepage.html +894 -90
- nautobot/project-static/docs/development/core/index.html +905 -90
- nautobot/project-static/docs/development/core/model-checklist.html +903 -91
- nautobot/project-static/docs/development/core/model-features.html +894 -90
- nautobot/project-static/docs/development/core/natural-keys.html +894 -90
- nautobot/project-static/docs/development/core/navigation-menu.html +894 -90
- nautobot/project-static/docs/development/core/release-checklist.html +897 -93
- nautobot/project-static/docs/development/core/role-internals.html +894 -90
- nautobot/project-static/docs/development/core/settings.html +894 -90
- nautobot/project-static/docs/development/core/style-guide.html +895 -91
- nautobot/project-static/docs/development/core/templates.html +906 -91
- nautobot/project-static/docs/development/core/testing.html +894 -90
- nautobot/project-static/docs/development/core/user-preferences.html +894 -90
- nautobot/project-static/docs/development/index.html +894 -90
- nautobot/project-static/docs/development/jobs/index.html +1271 -453
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +894 -90
- nautobot/project-static/docs/index.html +9032 -13
- 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 +902 -91
- nautobot/project-static/docs/overview/design_philosophy.html +896 -92
- nautobot/project-static/docs/overview/index.html +13 -8228
- nautobot/project-static/docs/release-notes/index.html +1131 -94
- nautobot/project-static/docs/release-notes/version-1.0.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.1.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.2.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.3.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.4.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.5.html +895 -91
- nautobot/project-static/docs/release-notes/version-1.6.html +895 -91
- nautobot/project-static/docs/release-notes/version-2.0.html +894 -90
- nautobot/project-static/docs/release-notes/version-2.1.html +894 -90
- nautobot/project-static/docs/release-notes/version-2.2.html +1137 -196
- 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 +335 -260
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +1025 -175
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +902 -90
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +946 -155
- nautobot/project-static/docs/user-guide/administration/installation/index.html +903 -95
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +936 -124
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +956 -159
- nautobot/project-static/docs/user-guide/administration/installation/services.html +915 -114
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +910 -101
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +894 -90
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +894 -90
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +894 -90
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +977 -121
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +895 -91
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +898 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +897 -93
- 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 +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +920 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +915 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +922 -118
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +923 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +916 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +928 -110
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +938 -120
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +930 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +939 -121
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +930 -112
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +923 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +925 -117
- 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 +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +942 -85
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +945 -88
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +931 -127
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +938 -90
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +899 -91
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +899 -91
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +903 -98
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +899 -91
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +894 -90
- 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 +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +894 -90
- nautobot/project-static/docs/user-guide/index.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1260 -787
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +898 -90
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +952 -123
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +894 -90
- 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 +12 -8
- 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.8.dist-info → nautobot-2.3.0.dist-info}/METADATA +21 -19
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/RECORD +684 -564
- 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.8.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
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)
|
nautobot/core/views/paginator.py
CHANGED
|
@@ -41,13 +41,14 @@ class EnhancedPage(Page):
|
|
|
41
41
|
return page_list
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def get_paginate_count(request):
|
|
44
|
+
def get_paginate_count(request, saved_view=None):
|
|
45
45
|
"""
|
|
46
46
|
Determine the length of a page, using the following in order:
|
|
47
47
|
|
|
48
48
|
1. per_page URL query parameter
|
|
49
|
-
2. Saved
|
|
50
|
-
3.
|
|
49
|
+
2. Saved view config
|
|
50
|
+
3. Saved user preference
|
|
51
|
+
4. PAGINATE_COUNT global setting.
|
|
51
52
|
"""
|
|
52
53
|
if "per_page" in request.GET:
|
|
53
54
|
try:
|
|
@@ -58,6 +59,11 @@ def get_paginate_count(request):
|
|
|
58
59
|
except ValueError:
|
|
59
60
|
pass
|
|
60
61
|
|
|
62
|
+
if saved_view is not None:
|
|
63
|
+
per_page = saved_view.config.get("pagination_count", None)
|
|
64
|
+
if per_page:
|
|
65
|
+
return per_page
|
|
66
|
+
|
|
61
67
|
if request.user.is_authenticated:
|
|
62
68
|
return request.user.get_config("pagination.per_page", config.get_settings_or_config("PAGINATE_COUNT"))
|
|
63
69
|
return config.get_settings_or_config("PAGINATE_COUNT")
|
nautobot/core/views/renderers.py
CHANGED
|
@@ -2,7 +2,9 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from django.contrib import messages
|
|
4
4
|
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
5
6
|
from django.db.models import Q
|
|
7
|
+
from django.template import engines, loader
|
|
6
8
|
from django_tables2 import RequestConfig
|
|
7
9
|
from rest_framework import renderers
|
|
8
10
|
|
|
@@ -14,17 +16,20 @@ from nautobot.core.forms import (
|
|
|
14
16
|
from nautobot.core.forms.forms import DynamicFilterFormSet
|
|
15
17
|
from nautobot.core.templatetags.helpers import bettertitle, validated_viewname
|
|
16
18
|
from nautobot.core.utils.config import get_settings_or_config
|
|
17
|
-
from nautobot.core.utils.lookup import get_created_and_last_updated_usernames_for_model
|
|
18
19
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
19
20
|
from nautobot.core.utils.requests import (
|
|
20
21
|
convert_querydict_to_factory_formset_acceptable_querydict,
|
|
21
22
|
normalize_querydict,
|
|
22
23
|
)
|
|
23
24
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
24
|
-
from nautobot.core.views.utils import
|
|
25
|
+
from nautobot.core.views.utils import (
|
|
26
|
+
check_filter_for_display,
|
|
27
|
+
common_detail_view_context,
|
|
28
|
+
get_csv_form_fields_from_serializer_class,
|
|
29
|
+
view_changes_not_saved,
|
|
30
|
+
)
|
|
31
|
+
from nautobot.extras.models import SavedView
|
|
25
32
|
from nautobot.extras.models.change_logging import ObjectChange
|
|
26
|
-
from nautobot.extras.tables import AssociatedContactsTable
|
|
27
|
-
from nautobot.extras.utils import get_base_template
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
@@ -34,6 +39,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
34
39
|
|
|
35
40
|
# Log error messages within NautobotHTMLRenderer
|
|
36
41
|
logger = logging.getLogger(__name__)
|
|
42
|
+
saved_view = None
|
|
43
|
+
|
|
44
|
+
exception_template_names = ["%(status_code)s.html"]
|
|
37
45
|
|
|
38
46
|
def get_dynamic_filter_form(self, view, request, *args, filterset_class=None, **kwargs):
|
|
39
47
|
"""
|
|
@@ -44,7 +52,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
44
52
|
filterset = None
|
|
45
53
|
if filterset_class:
|
|
46
54
|
filterset = filterset_class()
|
|
47
|
-
factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
|
|
55
|
+
factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
|
|
56
|
+
view.filter_params if view.filter_params is not None else request.GET, filterset
|
|
57
|
+
)
|
|
48
58
|
return DynamicFilterFormSet(filterset=filterset, data=factory_formset_params)
|
|
49
59
|
|
|
50
60
|
def construct_user_permissions(self, request, model):
|
|
@@ -64,14 +74,32 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
64
74
|
"""
|
|
65
75
|
table_class = view.get_table_class()
|
|
66
76
|
request = kwargs.get("request", view.request)
|
|
77
|
+
saved_view_pk = request.GET.get("saved_view", None)
|
|
78
|
+
table_changes_pending = request.GET.get("table_changes_pending", False)
|
|
67
79
|
queryset = view.alter_queryset(request)
|
|
68
80
|
|
|
69
81
|
if view.action in ["list", "notes", "changelog"]:
|
|
70
82
|
if view.action == "list":
|
|
71
83
|
permissions = kwargs.get("permissions", {})
|
|
72
|
-
|
|
84
|
+
self.saved_view = None
|
|
85
|
+
if saved_view_pk is not None:
|
|
86
|
+
try:
|
|
87
|
+
# We are not using .restrict(request.user, "view") here
|
|
88
|
+
# User should be able to see any saved view that he has the list view access to.
|
|
89
|
+
self.saved_view = SavedView.objects.get(pk=saved_view_pk)
|
|
90
|
+
except ObjectDoesNotExist:
|
|
91
|
+
pass
|
|
92
|
+
if view.request.GET.getlist("sort") or (
|
|
93
|
+
self.saved_view is not None and self.saved_view.config.get("sort_order")
|
|
94
|
+
):
|
|
73
95
|
view.hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
|
|
74
|
-
table = table_class(
|
|
96
|
+
table = table_class(
|
|
97
|
+
queryset,
|
|
98
|
+
table_changes_pending=table_changes_pending,
|
|
99
|
+
saved_view=self.saved_view,
|
|
100
|
+
user=request.user,
|
|
101
|
+
hide_hierarchy_ui=view.hide_hierarchy_ui,
|
|
102
|
+
)
|
|
75
103
|
if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
|
|
76
104
|
table.columns.show("pk")
|
|
77
105
|
elif view.action == "notes":
|
|
@@ -93,7 +121,7 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
93
121
|
# Apply the request context
|
|
94
122
|
paginate = {
|
|
95
123
|
"paginator_class": EnhancedPaginator,
|
|
96
|
-
"per_page": get_paginate_count(request),
|
|
124
|
+
"per_page": get_paginate_count(request, self.saved_view),
|
|
97
125
|
}
|
|
98
126
|
max_page_size = get_settings_or_config("MAX_PAGE_SIZE")
|
|
99
127
|
if max_page_size and paginate["per_page"] > max_page_size:
|
|
@@ -129,6 +157,30 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
129
157
|
messages.error(request, f"Missing views for action(s) {', '.join(invalid_actions)}")
|
|
130
158
|
return valid_actions
|
|
131
159
|
|
|
160
|
+
def get_template_context(self, data, renderer_context):
|
|
161
|
+
# borrowed from rest_framework's TemplateHTMLRenderer - should our html renderer be based on that class?
|
|
162
|
+
response = renderer_context["response"]
|
|
163
|
+
if response.exception:
|
|
164
|
+
data["status_code"] = response.status_code
|
|
165
|
+
return data
|
|
166
|
+
|
|
167
|
+
def get_exception_template(self, response):
|
|
168
|
+
# borrowed from rest_framework's TemplateHTMLRenderer - remove if switching base class
|
|
169
|
+
template_names = [name % {"status_code": response.status_code} for name in self.exception_template_names]
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Try to find an appropriate error template
|
|
173
|
+
return self.resolve_template(template_names)
|
|
174
|
+
except Exception:
|
|
175
|
+
# Fall back to using eg '404 Not Found'
|
|
176
|
+
body = f"{response.status_code} {response.status_text.title()}"
|
|
177
|
+
template = engines["django"].from_string(body)
|
|
178
|
+
return template
|
|
179
|
+
|
|
180
|
+
def resolve_template(self, template_names):
|
|
181
|
+
# borrowed from rest_framework's TemplateHTMLRenderer - remove if switching base class
|
|
182
|
+
return loader.select_template(template_names)
|
|
183
|
+
|
|
132
184
|
def get_context(self, data, accepted_media_type, renderer_context):
|
|
133
185
|
"""
|
|
134
186
|
Override get_context() from BrowsableAPIRenderer to obtain the context data we need to render our templates.
|
|
@@ -157,7 +209,7 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
157
209
|
display_filter_params = []
|
|
158
210
|
# Compile a dictionary indicating which permissions are available to the current user for this model
|
|
159
211
|
permissions = self.construct_user_permissions(request, model)
|
|
160
|
-
if view.action
|
|
212
|
+
if view.detail or view.action == "create":
|
|
161
213
|
instance = view.get_object()
|
|
162
214
|
return_url = view.get_return_url(request, instance)
|
|
163
215
|
else:
|
|
@@ -179,9 +231,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
179
231
|
for field_name, values in view.filter_params.items()
|
|
180
232
|
]
|
|
181
233
|
if view.filterset_form_class is not None:
|
|
182
|
-
filter_form = view.filterset_form_class(
|
|
234
|
+
filter_form = view.filterset_form_class(view.filter_params, label_suffix="")
|
|
183
235
|
table = self.construct_table(view, request=request, permissions=permissions)
|
|
184
|
-
search_form = SearchForm(data=
|
|
236
|
+
search_form = SearchForm(data=view.filter_params)
|
|
185
237
|
elif view.action == "destroy":
|
|
186
238
|
form = form_class(initial=request.GET)
|
|
187
239
|
elif view.action in ["create", "update"]:
|
|
@@ -238,52 +290,56 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
238
290
|
"verbose_name_plural": queryset.model._meta.verbose_name_plural,
|
|
239
291
|
}
|
|
240
292
|
if view.action == "retrieve":
|
|
241
|
-
|
|
293
|
+
context.update(common_detail_view_context(request, instance))
|
|
294
|
+
elif view.action == "list":
|
|
295
|
+
# Construct valid actions for list view.
|
|
296
|
+
valid_actions = self.validate_action_buttons(view, request)
|
|
297
|
+
# Query SavedViews for dropdown button
|
|
298
|
+
list_url = validated_viewname(model, "list")
|
|
299
|
+
saved_views = None
|
|
300
|
+
if model.is_saved_view_model:
|
|
301
|
+
# We are not using .restrict(request.user, "view") here
|
|
302
|
+
# User should be able to see any saved view that he has the list view access to.
|
|
303
|
+
if request.user.has_perms(["extras.view_savedview"]):
|
|
304
|
+
saved_views = SavedView.objects.filter(view=list_url).order_by("name").only("pk", "name")
|
|
305
|
+
else:
|
|
306
|
+
shared_saved_views = (
|
|
307
|
+
SavedView.objects.filter(view=list_url, is_shared=True).order_by("name").only("pk", "name")
|
|
308
|
+
)
|
|
309
|
+
user_owned_saved_views = (
|
|
310
|
+
SavedView.objects.filter(view=list_url, owner=request.user).order_by("name").only("pk", "name")
|
|
311
|
+
)
|
|
312
|
+
saved_views = shared_saved_views | user_owned_saved_views
|
|
242
313
|
|
|
243
|
-
|
|
244
|
-
context
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
elif view.action == "bulk_create": # 3.0 TODO: remove, replaced by ImportObjects system Job
|
|
273
|
-
context.update(
|
|
274
|
-
{
|
|
275
|
-
"active_tab": view.bulk_create_active_tab if view.bulk_create_active_tab else "csv-data",
|
|
276
|
-
"fields": get_csv_form_fields_from_serializer_class(view.serializer_class),
|
|
277
|
-
}
|
|
278
|
-
)
|
|
279
|
-
elif view.action in ["changelog", "notes"]:
|
|
280
|
-
context.update(
|
|
281
|
-
{
|
|
282
|
-
"base_template": get_base_template(data.get("base_template"), model),
|
|
283
|
-
"active_tab": view.action,
|
|
284
|
-
}
|
|
285
|
-
)
|
|
286
|
-
context.update(view.get_extra_context(request, instance=None))
|
|
314
|
+
new_changes_not_applied = view_changes_not_saved(request, view, self.saved_view)
|
|
315
|
+
context.update(
|
|
316
|
+
{
|
|
317
|
+
"model": model,
|
|
318
|
+
"current_saved_view": self.saved_view,
|
|
319
|
+
"new_changes_not_applied": new_changes_not_applied,
|
|
320
|
+
"action_buttons": valid_actions,
|
|
321
|
+
"list_url": list_url,
|
|
322
|
+
"saved_views": saved_views,
|
|
323
|
+
"title": bettertitle(model._meta.verbose_name_plural),
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
elif view.action in ["create", "update"]:
|
|
327
|
+
context.update(
|
|
328
|
+
{
|
|
329
|
+
"editing": instance.present_in_database,
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
elif view.action == "bulk_create": # 3.0 TODO: remove, replaced by ImportObjects system Job
|
|
333
|
+
context.update(
|
|
334
|
+
{
|
|
335
|
+
"active_tab": view.bulk_create_active_tab if view.bulk_create_active_tab else "csv-data",
|
|
336
|
+
"fields": get_csv_form_fields_from_serializer_class(view.serializer_class),
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Ensure the proper inheritance of context variables is applied: the view's returned data takes priority over the viewset's get_extra_context
|
|
341
|
+
context.update(view.get_extra_context(request, instance))
|
|
342
|
+
context.update(self.get_template_context(data, renderer_context))
|
|
287
343
|
return context
|
|
288
344
|
|
|
289
345
|
def render(self, data, accepted_media_type=None, renderer_context=None):
|
|
@@ -291,6 +347,15 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
|
|
|
291
347
|
Overrode render() from BrowsableAPIRenderer to set self.template with NautobotViewSet's get_template_name() before it is rendered.
|
|
292
348
|
"""
|
|
293
349
|
view = renderer_context["view"]
|
|
350
|
+
request = renderer_context["request"]
|
|
351
|
+
response = renderer_context["response"]
|
|
352
|
+
|
|
353
|
+
# TODO: borrowed from TemplateHTMLRenderer. Remove when switching base class
|
|
354
|
+
if response.exception:
|
|
355
|
+
template = self.get_exception_template(response)
|
|
356
|
+
context = self.get_context(data, accepted_media_type, renderer_context)
|
|
357
|
+
return template.render(context, request=request)
|
|
358
|
+
|
|
294
359
|
# Get the corresponding template based on self.action in view.get_template_name() unless it is already specified in the Response() data.
|
|
295
360
|
# See form_valid() for self.action == "bulk_create".
|
|
296
361
|
self.template = data.get("template", view.get_template_name())
|