nautobot 2.4.17__py3-none-any.whl → 2.4.19__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/ui.py +6 -0
- nautobot/apps/views.py +2 -0
- nautobot/circuits/tables.py +1 -1
- nautobot/circuits/templates/circuits/circuit_create.html +7 -7
- nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -5
- nautobot/circuits/templates/circuits/circuittermination_create.html +26 -26
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -8
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +20 -20
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +3 -3
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +9 -0
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +1 -3
- nautobot/circuits/tests/integration/test_circuit.py +2 -2
- nautobot/circuits/views.py +49 -15
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +1 -4
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +1 -7
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +1 -3
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +1 -5
- nautobot/cloud/views.py +45 -0
- nautobot/core/filters.py +2 -2
- nautobot/core/graphql/generators.py +5 -2
- nautobot/core/jobs/bulk_actions.py +48 -85
- nautobot/core/models/querysets.py +2 -1
- nautobot/core/settings.py +1 -0
- nautobot/core/settings.yaml +9 -0
- nautobot/core/tables.py +21 -23
- nautobot/core/templates/40x.html +15 -15
- nautobot/core/templates/500.html +21 -21
- nautobot/core/templates/admin/app_index.html +8 -8
- nautobot/core/templates/admin/base.html +104 -104
- nautobot/core/templates/admin/change_form.html +65 -65
- nautobot/core/templates/admin/change_list.html +60 -60
- nautobot/core/templates/admin/change_list_results.html +39 -39
- nautobot/core/templates/admin/config/config.html +47 -47
- nautobot/core/templates/admin/delete_confirmation.html +47 -47
- nautobot/core/templates/admin/edit_inline/stacked.html +124 -124
- nautobot/core/templates/admin/edit_inline/tabular.html +60 -60
- nautobot/core/templates/admin/includes/fieldset.html +4 -4
- nautobot/core/templates/admin/index.html +60 -60
- nautobot/core/templates/admin/prepopulated_fields_js.html +18 -18
- nautobot/core/templates/admin/submit_line.html +4 -4
- nautobot/core/templates/base_django.html +46 -46
- nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +8 -8
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +8 -8
- nautobot/core/templates/buttons/export.html +3 -3
- nautobot/core/templates/components/breadcrumbs.html +19 -0
- nautobot/core/templates/components/button/default.html +3 -3
- nautobot/core/templates/components/button/dropdown.html +7 -7
- nautobot/core/templates/components/button/formbutton.html +4 -4
- nautobot/core/templates/components/panel/body_content_data_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +3 -0
- nautobot/core/templates/components/panel/footer_content_table.html +3 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +10 -1
- nautobot/core/templates/components/tab/content_wrapper.html +1 -1
- nautobot/core/templates/components/tab/label_wrapper.html +1 -1
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +10 -3
- nautobot/core/templates/generic/object_bulk_add_component.html +40 -40
- nautobot/core/templates/generic/object_bulk_create.html +3 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +6 -6
- nautobot/core/templates/generic/object_bulk_update.html +52 -52
- nautobot/core/templates/generic/object_changelog.html +0 -2
- nautobot/core/templates/generic/object_import.html +33 -33
- nautobot/core/templates/generic/object_list.html +271 -268
- nautobot/core/templates/generic/object_notes.html +0 -2
- nautobot/core/templates/generic/object_retrieve.html +264 -257
- nautobot/core/templates/graphene/graphiql.html +127 -127
- nautobot/core/templates/home.html +62 -62
- nautobot/core/templates/inc/computed_fields/panel_data.html +13 -13
- nautobot/core/templates/inc/created_updated.html +8 -8
- nautobot/core/templates/inc/custom_fields/panel_data.html +13 -13
- nautobot/core/templates/inc/dynamic_groups_panel.html +11 -11
- nautobot/core/templates/inc/footer.html +19 -19
- nautobot/core/templates/inc/javascript.html +1 -1
- nautobot/core/templates/inc/media.html +46 -46
- nautobot/core/templates/inc/nav_menu.html +1 -1
- nautobot/core/templates/inc/relationships_table_rows.html +22 -22
- nautobot/core/templates/inc/tenant_table_row.html +1 -1
- nautobot/core/templates/login.html +77 -77
- nautobot/core/templates/media_failure.html +38 -38
- nautobot/core/templates/panel_table.html +1 -1
- nautobot/core/templates/rest_framework/api.html +3 -3
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +9 -9
- nautobot/core/templates/utilities/confirmation_form.html +18 -18
- nautobot/core/templates/utilities/render_field.html +1 -1
- nautobot/core/templates/utilities/render_jinja2.html +43 -43
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +56 -56
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +799 -799
- nautobot/core/templates/utilities/worker_status.html +122 -122
- nautobot/core/templates/widgets/clearable_file.html +3 -3
- nautobot/core/templates/widgets/sluginput.html +1 -1
- nautobot/core/templatetags/buttons.py +8 -2
- nautobot/core/templatetags/helpers.py +24 -0
- nautobot/core/templatetags/ui_framework.py +40 -5
- nautobot/core/testing/filters.py +37 -21
- nautobot/core/testing/integration.py +7 -4
- nautobot/core/testing/views.py +49 -5
- nautobot/core/tests/test_breadcrumbs.py +78 -4
- nautobot/core/tests/test_commands.py +7 -4
- nautobot/core/tests/test_graphql.py +20 -5
- nautobot/core/tests/test_jobs.py +34 -21
- nautobot/core/tests/test_tables.py +43 -6
- nautobot/core/tests/test_templatetags_ui_framework.py +146 -0
- nautobot/core/tests/test_titles.py +2 -2
- nautobot/core/tests/test_ui.py +188 -1
- nautobot/core/tests/test_utils.py +35 -0
- nautobot/core/tests/test_views.py +45 -0
- nautobot/core/tests/test_views_generic.py +43 -0
- nautobot/core/tests/test_views_utils.py +239 -5
- nautobot/core/ui/breadcrumbs.py +220 -28
- nautobot/core/ui/bulk_buttons.py +8 -0
- nautobot/core/ui/object_detail.py +181 -60
- nautobot/core/ui/titles.py +10 -5
- nautobot/core/utils/requests.py +27 -2
- nautobot/core/views/__init__.py +24 -3
- nautobot/core/views/generic.py +70 -35
- nautobot/core/views/mixins.py +226 -122
- nautobot/core/views/utils.py +270 -1
- nautobot/dcim/api/serializers.py +8 -2
- nautobot/dcim/constants.py +1 -0
- nautobot/dcim/factory.py +4 -3
- nautobot/dcim/filters/mixins.py +1 -2
- nautobot/dcim/forms.py +5 -1
- nautobot/dcim/migrations/0074_alter_rack_u_height.py +21 -0
- nautobot/dcim/models/devices.py +30 -1
- nautobot/dcim/models/racks.py +2 -2
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +24 -0
- nautobot/dcim/tables/power.py +2 -2
- nautobot/dcim/templates/dcim/cable.html +53 -53
- nautobot/dcim/templates/dcim/cable_connect.html +182 -182
- nautobot/dcim/templates/dcim/cable_trace.html +1 -1
- nautobot/dcim/templates/dcim/console_port_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/consoleport.html +86 -86
- nautobot/dcim/templates/dcim/consoleserverport.html +86 -86
- nautobot/dcim/templates/dcim/controller_create.html +34 -34
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -68
- nautobot/dcim/templates/dcim/device/base.html +1 -114
- nautobot/dcim/templates/dcim/device/config.html +17 -17
- nautobot/dcim/templates/dcim/device/consoleports.html +1 -52
- nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -52
- nautobot/dcim/templates/dcim/device/devicebays.html +1 -48
- nautobot/dcim/templates/dcim/device/frontports.html +1 -52
- nautobot/dcim/templates/dcim/device/interfaces.html +1 -56
- nautobot/dcim/templates/dcim/device/inventory.html +1 -48
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +64 -64
- nautobot/dcim/templates/dcim/device/modulebays.html +1 -48
- nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -52
- nautobot/dcim/templates/dcim/device/powerports.html +1 -52
- nautobot/dcim/templates/dcim/device/rearports.html +1 -52
- nautobot/dcim/templates/dcim/device/status.html +66 -66
- nautobot/dcim/templates/dcim/device/wireless.html +1 -72
- nautobot/dcim/templates/dcim/device.html +4 -422
- nautobot/dcim/templates/dcim/device_component.html +0 -19
- nautobot/dcim/templates/dcim/device_component_add.html +25 -25
- nautobot/dcim/templates/dcim/device_create.html +229 -0
- nautobot/dcim/templates/dcim/device_edit.html +2 -227
- nautobot/dcim/templates/dcim/devicebay.html +41 -41
- nautobot/dcim/templates/dcim/devicebay_populate.html +32 -32
- nautobot/dcim/templates/dcim/devicetype_component_add.html +28 -28
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -3
- nautobot/dcim/templates/dcim/frontport.html +84 -84
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +8 -0
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +1 -15
- nautobot/dcim/templates/dcim/inc/location_hierarchy.html +22 -22
- nautobot/dcim/templates/dcim/interface.html +206 -206
- nautobot/dcim/templates/dcim/interface_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +6 -6
- nautobot/dcim/templates/dcim/inventoryitem.html +44 -44
- nautobot/dcim/templates/dcim/inventoryitem_add.html +32 -32
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +22 -22
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +46 -46
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -7
- nautobot/dcim/templates/dcim/locationtype.html +1 -6
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -7
- nautobot/dcim/templates/dcim/module/base.html +85 -85
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_retrieve.html +52 -52
- nautobot/dcim/templates/dcim/module_update.html +61 -61
- nautobot/dcim/templates/dcim/modulebay_destroy.html +1 -1
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +83 -99
- nautobot/dcim/templates/dcim/modulebay_update.html +33 -33
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +140 -144
- nautobot/dcim/templates/dcim/platform_create.html +38 -38
- nautobot/dcim/templates/dcim/power_port_connection_list.html +5 -5
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -8
- nautobot/dcim/templates/dcim/poweroutlet.html +85 -85
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +1 -8
- nautobot/dcim/templates/dcim/powerport.html +91 -91
- nautobot/dcim/templates/dcim/rack_elevation_list.html +18 -18
- nautobot/dcim/templates/dcim/rack_retrieve.html +264 -274
- nautobot/dcim/templates/dcim/rackreservation_retrieve.html +0 -3
- nautobot/dcim/templates/dcim/rearport.html +78 -78
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -50
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -5
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +3 -2
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +6 -2
- nautobot/dcim/tests/test_api.py +33 -1
- nautobot/dcim/tests/test_views.py +189 -4
- nautobot/dcim/ui.py +29 -0
- nautobot/dcim/urls.py +1 -109
- nautobot/dcim/utils.py +30 -0
- nautobot/dcim/views.py +1149 -550
- nautobot/extras/filters/mixins.py +1 -1
- nautobot/extras/forms/forms.py +15 -0
- nautobot/extras/models/groups.py +10 -1
- nautobot/extras/models/jobs.py +2 -2
- nautobot/extras/plugins/views.py +18 -5
- nautobot/extras/tables.py +24 -2
- nautobot/extras/templates/extras/computedfield_edit.html +4 -4
- nautobot/extras/templates/extras/configcontext_update.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +32 -32
- nautobot/extras/templates/extras/customfield_retrieve.html +1 -128
- nautobot/extras/templates/extras/customfield_update.html +23 -23
- nautobot/extras/templates/extras/dynamicgroup.html +2 -99
- nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -199
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +99 -0
- nautobot/extras/templates/extras/dynamicgroup_update.html +199 -0
- nautobot/extras/templates/extras/gitrepository.html +2 -82
- nautobot/extras/templates/extras/gitrepository_list.html +10 -10
- nautobot/extras/templates/extras/gitrepository_object_edit.html +2 -13
- nautobot/extras/templates/extras/gitrepository_retrieve.html +82 -0
- nautobot/extras/templates/extras/gitrepository_update.html +13 -0
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +73 -73
- nautobot/extras/templates/extras/inc/configcontext_format.html +2 -2
- nautobot/extras/templates/extras/inc/job_table.html +10 -10
- nautobot/extras/templates/extras/inc/jobresult.html +21 -21
- nautobot/extras/templates/extras/inc/jobresult_js.html +6 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +10 -10
- nautobot/extras/templates/extras/job.html +64 -64
- nautobot/extras/templates/extras/job_approval_request.html +9 -9
- nautobot/extras/templates/extras/job_bulk_edit.html +13 -13
- nautobot/extras/templates/extras/job_edit.html +45 -45
- nautobot/extras/templates/extras/job_list.html +4 -4
- nautobot/extras/templates/extras/jobresult_retrieve.html +0 -25
- nautobot/extras/templates/extras/marketplace.html +101 -101
- nautobot/extras/templates/extras/metadatatype_create.html +20 -20
- nautobot/extras/templates/extras/note_retrieve.html +0 -52
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +18 -18
- nautobot/extras/templates/extras/object_configcontext.html +1 -3
- nautobot/extras/templates/extras/objectchange.html +2 -165
- nautobot/extras/templates/extras/objectchange_retrieve.html +165 -0
- nautobot/extras/templates/extras/plugin_detail.html +44 -48
- nautobot/extras/templates/extras/plugins_list.html +9 -11
- nautobot/extras/templates/extras/plugins_tiles.html +26 -26
- nautobot/extras/templates/extras/relationship_edit.html +4 -4
- nautobot/extras/templates/extras/role_retrieve.html +13 -13
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +21 -21
- nautobot/extras/templates/extras/scheduledjob.html +128 -128
- nautobot/extras/templates/extras/secret_create.html +53 -53
- nautobot/extras/templates/extras/secretsgroup_update.html +13 -13
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +3 -3
- nautobot/extras/templates/extras/webhook.html +79 -79
- nautobot/extras/tests/integration/test_relationships.py +6 -6
- nautobot/extras/tests/test_dynamicgroups.py +73 -18
- nautobot/extras/tests/test_filters.py +1 -1
- nautobot/extras/tests/test_jobs.py +2 -0
- nautobot/extras/tests/test_views.py +8 -3
- nautobot/extras/urls.py +3 -97
- nautobot/extras/views.py +524 -456
- nautobot/ipam/filters.py +2 -2
- nautobot/ipam/migrations/0053_alter_vrfdeviceassignment_options_and_more.py +20 -0
- nautobot/ipam/models.py +34 -0
- nautobot/ipam/querysets.py +3 -3
- nautobot/ipam/signals.py +6 -1
- nautobot/ipam/tables.py +3 -1
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +4 -0
- nautobot/ipam/templates/ipam/inc/toggle_available.html +8 -8
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +4 -4
- nautobot/ipam/templates/ipam/ipaddress.html +119 -123
- nautobot/ipam/templates/ipam/ipaddress_assign.html +10 -10
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -1
- nautobot/ipam/templates/ipam/ipaddress_merge.html +180 -180
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +48 -48
- nautobot/ipam/templates/ipam/prefix.html +2 -115
- nautobot/ipam/templates/ipam/prefix_create.html +34 -0
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -34
- nautobot/ipam/templates/ipam/prefix_retrieve.html +3 -0
- nautobot/ipam/templates/ipam/service_retrieve.html +1 -6
- nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -7
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -1
- nautobot/ipam/tests/test_api.py +5 -0
- nautobot/ipam/tests/test_models.py +387 -0
- nautobot/ipam/tests/test_querysets.py +46 -0
- nautobot/ipam/tests/test_views.py +34 -0
- nautobot/ipam/ui.py +145 -0
- nautobot/ipam/urls.py +1 -46
- nautobot/ipam/utils/__init__.py +26 -0
- nautobot/ipam/utils/migrations.py +1 -1
- nautobot/ipam/views.py +234 -112
- nautobot/project-static/docs/404.html +11 -11
- nautobot/project-static/docs/apps/index.html +11 -11
- nautobot/project-static/docs/apps/nautobot-apps.html +11 -11
- nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js → bundle.f55a23d4.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js.map → bundle.f55a23d4.min.js.map} +2 -2
- nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css → main.e53b48f4.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css.map → main.e53b48f4.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +83 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1265 -281
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +11 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +12 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +452 -29
- nautobot/project-static/docs/development/apps/api/configuration-view.html +11 -11
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/graphql.html +11 -11
- nautobot/project-static/docs/development/apps/api/models/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +12 -12
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +11 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +11 -11
- nautobot/project-static/docs/development/apps/api/prometheus.html +11 -11
- nautobot/project-static/docs/development/apps/api/setup.html +11 -11
- nautobot/project-static/docs/development/apps/api/testing.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +11 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/base-template.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/index.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/notes.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +11 -11
- nautobot/project-static/docs/development/apps/api/views/urls.html +11 -11
- nautobot/project-static/docs/development/apps/index.html +11 -11
- nautobot/project-static/docs/development/apps/migration/code-updates.html +11 -11
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +11 -11
- nautobot/project-static/docs/development/apps/migration/from-v1.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +11 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +11 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +11 -11
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +11 -11
- nautobot/project-static/docs/development/core/application-registry.html +11 -11
- nautobot/project-static/docs/development/core/best-practices.html +11 -11
- nautobot/project-static/docs/development/core/bootstrap-ui.html +11 -11
- nautobot/project-static/docs/development/core/caching.html +11 -11
- nautobot/project-static/docs/development/core/controllers.html +11 -11
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +11 -11
- nautobot/project-static/docs/development/core/generic-views.html +11 -11
- nautobot/project-static/docs/development/core/getting-started.html +50 -63
- nautobot/project-static/docs/development/core/homepage.html +11 -11
- nautobot/project-static/docs/development/core/index.html +11 -11
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +11 -11
- nautobot/project-static/docs/development/core/model-checklist.html +11 -11
- nautobot/project-static/docs/development/core/model-features.html +11 -11
- nautobot/project-static/docs/development/core/natural-keys.html +11 -11
- nautobot/project-static/docs/development/core/navigation-menu.html +11 -11
- nautobot/project-static/docs/development/core/release-checklist.html +11 -11
- nautobot/project-static/docs/development/core/role-internals.html +11 -11
- nautobot/project-static/docs/development/core/settings.html +11 -11
- nautobot/project-static/docs/development/core/style-guide.html +15 -11
- nautobot/project-static/docs/development/core/templates.html +11 -11
- nautobot/project-static/docs/development/core/testing.html +11 -11
- nautobot/project-static/docs/development/core/ui-component-framework.html +17 -22
- nautobot/project-static/docs/development/core/user-preferences.html +11 -11
- nautobot/project-static/docs/development/index.html +11 -11
- nautobot/project-static/docs/development/jobs/getting-started.html +11 -11
- nautobot/project-static/docs/development/jobs/index.html +11 -11
- nautobot/project-static/docs/development/jobs/installation.html +11 -11
- nautobot/project-static/docs/development/jobs/job-extensions.html +11 -11
- nautobot/project-static/docs/development/jobs/job-logging.html +11 -11
- nautobot/project-static/docs/development/jobs/job-patterns.html +11 -11
- nautobot/project-static/docs/development/jobs/job-structure.html +11 -11
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +11 -11
- nautobot/project-static/docs/development/jobs/testing.html +11 -11
- nautobot/project-static/docs/index.html +11 -11
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +11 -11
- nautobot/project-static/docs/overview/design_philosophy.html +11 -11
- nautobot/project-static/docs/release-notes/index.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.0.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.1.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.2.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.3.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.4.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.5.html +11 -11
- nautobot/project-static/docs/release-notes/version-1.6.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.0.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.1.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.2.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.3.html +11 -11
- nautobot/project-static/docs/release-notes/version-2.4.html +418 -11
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +300 -300
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +11 -11
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +38 -11
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +89 -14
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +11 -11
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +11 -11
- nautobot/project-static/docs/user-guide/administration/installation/services.html +11 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +11 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +11 -11
- nautobot/project-static/docs/user-guide/administration/security/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/security/notices.html +11 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +11 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +11 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +11 -11
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +11 -11
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +11 -11
- nautobot/project-static/docs/user-guide/index.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +11 -11
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +11 -11
- nautobot/project-static/img/nautobot_icon.svg +32 -34
- nautobot/project-static/js/table_sorting_indicator.js +0 -2
- nautobot/tenancy/templates/tenancy/tenant.html +1 -7
- nautobot/tenancy/views.py +13 -0
- nautobot/users/templates/users/api_tokens.html +4 -4
- nautobot/users/templates/users/base.html +28 -28
- nautobot/virtualization/templates/virtualization/cluster.html +64 -64
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +8 -0
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +25 -25
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +1 -251
- nautobot/virtualization/templates/virtualization/vminterface.html +70 -70
- nautobot/virtualization/urls.py +0 -12
- nautobot/virtualization/views.py +158 -54
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +13 -13
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -1
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/METADATA +4 -4
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/RECORD +623 -607
- nautobot/core/templates/inc/breadcrumbs.html +0 -14
- nautobot/ipam/templates/ipam/prefix_ipaddresses.html +0 -11
- nautobot/ipam/templates/ipam/prefix_prefixes.html +0 -11
- nautobot/project-static/docs/requirements.txt +0 -14
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/NOTICE +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/WHEEL +0 -0
- {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/entry_points.txt +0 -0
nautobot/core/ui/bulk_buttons.py
CHANGED
|
@@ -45,6 +45,14 @@ class BulkEditButton(BaseBulkButton):
|
|
|
45
45
|
weight = 300
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
class BulkDisconnectButton(BaseBulkButton):
|
|
49
|
+
action = "disconnect"
|
|
50
|
+
color = ButtonActionColorChoices.DISCONNECT
|
|
51
|
+
icon = "mdi-ethernet-cable-off"
|
|
52
|
+
label = "Disconnect"
|
|
53
|
+
weight = 350
|
|
54
|
+
|
|
55
|
+
|
|
48
56
|
class BulkDeleteButton(BaseBulkButton):
|
|
49
57
|
action = "delete"
|
|
50
58
|
color = ButtonActionColorChoices.DELETE
|
|
@@ -9,6 +9,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
9
9
|
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
|
|
10
10
|
from django.db import models
|
|
11
11
|
from django.db.models import CharField, JSONField, Q, URLField
|
|
12
|
+
from django.db.models.constants import LOOKUP_SEP
|
|
12
13
|
from django.db.models.fields.related import ManyToManyField
|
|
13
14
|
from django.template import Context
|
|
14
15
|
from django.template.defaultfilters import truncatechars
|
|
@@ -42,9 +43,11 @@ from nautobot.core.utils.lookup import get_filterset_for_model, get_route_for_mo
|
|
|
42
43
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
43
44
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
44
45
|
from nautobot.core.views.utils import get_obj_from_context
|
|
46
|
+
from nautobot.dcim.models import Rack
|
|
45
47
|
from nautobot.extras.choices import CustomFieldTypeChoices
|
|
46
48
|
from nautobot.extras.tables import AssociatedContactsTable, DynamicGroupTable, ObjectMetadataTable
|
|
47
49
|
from nautobot.tenancy.models import Tenant
|
|
50
|
+
from nautobot.virtualization.models import Cluster
|
|
48
51
|
|
|
49
52
|
logger = logging.getLogger(__name__)
|
|
50
53
|
|
|
@@ -122,16 +125,24 @@ class ObjectDetailContent:
|
|
|
122
125
|
class Component:
|
|
123
126
|
"""Common base class for renderable components (tabs, panels, etc.)."""
|
|
124
127
|
|
|
125
|
-
def __init__(
|
|
128
|
+
def __init__(
|
|
129
|
+
self,
|
|
130
|
+
*,
|
|
131
|
+
weight,
|
|
132
|
+
required_permissions=None,
|
|
133
|
+
):
|
|
126
134
|
"""Initialize common Component properties.
|
|
127
135
|
|
|
128
136
|
Args:
|
|
129
137
|
weight (int): A relative weighting of this Component relative to its peers. Typically lower weights will be
|
|
130
138
|
rendered "first", usually towards the top left of the page.
|
|
139
|
+
required_permissions (list, optional): Permissions such as `["dcim.add_consoleport"]`.
|
|
140
|
+
The component will only be rendered if the user has these permissions.
|
|
131
141
|
"""
|
|
132
142
|
self.weight = weight
|
|
143
|
+
self.required_permissions = required_permissions or []
|
|
133
144
|
|
|
134
|
-
def should_render(self, context:
|
|
145
|
+
def should_render(self, context: Context):
|
|
135
146
|
"""
|
|
136
147
|
Check whether this component should be rendered at all.
|
|
137
148
|
|
|
@@ -139,9 +150,13 @@ class Component:
|
|
|
139
150
|
In general most Components may also return an empty string when actually rendered, which is typically also a
|
|
140
151
|
means to specify that they do not need to be rendered, but may be more expensive to derive.
|
|
141
152
|
|
|
153
|
+
The default implementation here checks for `self.required_permissions` if any.
|
|
154
|
+
|
|
142
155
|
Returns:
|
|
143
156
|
(bool): `True` (default) if this component should be rendered.
|
|
144
157
|
"""
|
|
158
|
+
if self.required_permissions:
|
|
159
|
+
return context["request"].user.has_perms(self.required_permissions)
|
|
145
160
|
return True
|
|
146
161
|
|
|
147
162
|
def render(self, context: Context):
|
|
@@ -177,12 +192,12 @@ class Button(Component):
|
|
|
177
192
|
link_name=None,
|
|
178
193
|
icon=None,
|
|
179
194
|
template_path="components/button/default.html",
|
|
180
|
-
required_permissions=None,
|
|
181
195
|
javascript_template_path=None,
|
|
182
196
|
attributes=None,
|
|
183
197
|
size=None,
|
|
184
198
|
link_includes_pk=True,
|
|
185
199
|
context_object_key=None,
|
|
200
|
+
render_on_tab_id="main",
|
|
186
201
|
**kwargs,
|
|
187
202
|
):
|
|
188
203
|
"""
|
|
@@ -198,30 +213,25 @@ class Button(Component):
|
|
|
198
213
|
context_object_key (str, optional): The key in the render context that will contain the linked object.
|
|
199
214
|
icon (str, optional): Material Design Icons icon, to include on the button, for example `"mdi-plus-bold"`.
|
|
200
215
|
template_path (str): Template to render for this button.
|
|
201
|
-
required_permissions (list, optional): Permissions such as `["dcim.add_consoleport"]`.
|
|
202
|
-
The button will only be rendered if the user has these permissions.
|
|
203
216
|
javascript_template_path (str, optional): JavaScript template to render and include with this button.
|
|
204
217
|
Does not need to include the wrapping `<script>...</script>` tags as those will be added automatically.
|
|
205
218
|
attributes (dict, optional): Additional HTML attributes and their values to attach to the button.
|
|
206
219
|
size (str, optional): The size of the button (e.g. `xs` or `sm`), used to apply a Bootstrap-style sizing.
|
|
220
|
+
render_on_tab_id (str, optional): The (only) tab that this button should appear on.
|
|
207
221
|
"""
|
|
208
222
|
self.label = label
|
|
209
223
|
self.color = color
|
|
210
224
|
self.link_name = link_name
|
|
211
225
|
self.icon = icon
|
|
212
226
|
self.template_path = template_path
|
|
213
|
-
self.required_permissions = required_permissions or []
|
|
214
227
|
self.javascript_template_path = javascript_template_path
|
|
215
228
|
self.attributes = attributes
|
|
216
229
|
self.size = size
|
|
217
230
|
self.link_includes_pk = link_includes_pk
|
|
218
231
|
self.context_object_key = context_object_key
|
|
232
|
+
self.render_on_tab_id = render_on_tab_id
|
|
219
233
|
super().__init__(**kwargs)
|
|
220
234
|
|
|
221
|
-
def should_render(self, context: Context):
|
|
222
|
-
"""Render if and only if the requesting user has appropriate permissions (if any)."""
|
|
223
|
-
return context["request"].user.has_perms(self.required_permissions)
|
|
224
|
-
|
|
225
235
|
def get_link(self, context: Context):
|
|
226
236
|
"""
|
|
227
237
|
Get the hyperlink URL (if any) for this button.
|
|
@@ -247,6 +257,11 @@ class Button(Component):
|
|
|
247
257
|
"size": self.size,
|
|
248
258
|
}
|
|
249
259
|
|
|
260
|
+
def should_render(self, context: Context):
|
|
261
|
+
if not super().should_render(context):
|
|
262
|
+
return False
|
|
263
|
+
return context.get("active_tab", "main") == self.render_on_tab_id
|
|
264
|
+
|
|
250
265
|
def render(self, context: Context):
|
|
251
266
|
"""Render this button to HTML, possibly including any associated JavaScript."""
|
|
252
267
|
if not self.should_render(context):
|
|
@@ -398,9 +413,18 @@ class Tab(Component):
|
|
|
398
413
|
"""
|
|
399
414
|
return self.label
|
|
400
415
|
|
|
416
|
+
def should_render_content(self, context: Context):
|
|
417
|
+
"""
|
|
418
|
+
Only render a main-view Tab if the active request is for the main object view rather than a separate action.
|
|
419
|
+
"""
|
|
420
|
+
request = context["request"]
|
|
421
|
+
obj = get_obj_from_context(context)
|
|
422
|
+
return request.path == obj.get_absolute_url()
|
|
423
|
+
|
|
401
424
|
def render(self, context: Context):
|
|
402
425
|
"""Render the tab's contents (layout and panels) to HTML."""
|
|
403
|
-
|
|
426
|
+
# Check should_render_content first as it's generally a cheaper calculation than should_render checking perms
|
|
427
|
+
if not self.should_render_content(context) or not self.should_render(context):
|
|
404
428
|
return ""
|
|
405
429
|
|
|
406
430
|
with context.update(
|
|
@@ -426,6 +450,7 @@ class DistinctViewTab(Tab):
|
|
|
426
450
|
url_name (str): The name of the URL pattern to link to, which will be reversed to generate the URL.
|
|
427
451
|
label_wrapper_template_path (str, optional): Template path to render the tab label to HTML.
|
|
428
452
|
related_object_attribute (str, optional): The name of the related object attribute to count for the tab label.
|
|
453
|
+
hide_if_empty (bool, optional): Do not render this tab if the related_object_attribute is an empty queryset.
|
|
429
454
|
"""
|
|
430
455
|
|
|
431
456
|
def __init__(
|
|
@@ -434,39 +459,62 @@ class DistinctViewTab(Tab):
|
|
|
434
459
|
url_name,
|
|
435
460
|
label_wrapper_template_path="components/tab/label_wrapper_distinct_view.html",
|
|
436
461
|
related_object_attribute="",
|
|
462
|
+
hide_if_empty=False,
|
|
437
463
|
**kwargs,
|
|
438
464
|
):
|
|
439
465
|
self.url_name = url_name
|
|
440
466
|
self.related_object_attribute = related_object_attribute
|
|
467
|
+
self.hide_if_empty = hide_if_empty
|
|
441
468
|
super().__init__(label_wrapper_template_path=label_wrapper_template_path, **kwargs)
|
|
442
469
|
|
|
443
470
|
def get_extra_context(self, context: Context):
|
|
444
471
|
return {"url": reverse(self.url_name, kwargs={"pk": get_obj_from_context(context).pk})}
|
|
445
472
|
|
|
446
|
-
def
|
|
473
|
+
def should_render(self, context: Context):
|
|
474
|
+
if not super().should_render(context):
|
|
475
|
+
return False
|
|
476
|
+
|
|
477
|
+
self.related_object_count = None
|
|
447
478
|
if not self.related_object_attribute:
|
|
448
|
-
return
|
|
479
|
+
return True
|
|
449
480
|
|
|
450
481
|
obj = get_obj_from_context(context)
|
|
451
482
|
if not hasattr(obj, self.related_object_attribute):
|
|
452
483
|
logger.warning(
|
|
453
484
|
f"{obj} does not have a related attribute {self.related_object_attribute} to count for tab label."
|
|
454
485
|
)
|
|
455
|
-
return
|
|
486
|
+
return True
|
|
456
487
|
|
|
457
488
|
try:
|
|
458
|
-
|
|
459
|
-
return format_html(
|
|
460
|
-
"{} {}",
|
|
461
|
-
self.label,
|
|
462
|
-
render_to_string("utilities/templatetags/badge.html", badge(related_obj_count, True)),
|
|
463
|
-
)
|
|
489
|
+
self.related_object_count = getattr(obj, self.related_object_attribute).count()
|
|
464
490
|
except AttributeError:
|
|
465
|
-
|
|
491
|
+
# Not a warning log, as there are cases where this is expected.
|
|
492
|
+
logger.debug(
|
|
466
493
|
f"{obj}'s attribute {self.related_object_attribute} is not a related manager to count for tab label."
|
|
467
494
|
)
|
|
495
|
+
|
|
496
|
+
if self.hide_if_empty and not self.related_object_count:
|
|
497
|
+
return False
|
|
498
|
+
return True
|
|
499
|
+
|
|
500
|
+
def should_render_content(self, context: Context):
|
|
501
|
+
"""
|
|
502
|
+
A DistinctViewTab should only render its content if the view in question is active.
|
|
503
|
+
"""
|
|
504
|
+
with context.update(self.get_extra_context(context)):
|
|
505
|
+
request = context["request"]
|
|
506
|
+
return request.path == context["url"]
|
|
507
|
+
|
|
508
|
+
def render_label(self, context: Context):
|
|
509
|
+
if not self.related_object_attribute or not self.related_object_count:
|
|
468
510
|
return super().render_label(context)
|
|
469
511
|
|
|
512
|
+
return format_html(
|
|
513
|
+
"{} {}",
|
|
514
|
+
self.label,
|
|
515
|
+
render_to_string("utilities/templatetags/badge.html", badge(self.related_object_count, True)),
|
|
516
|
+
)
|
|
517
|
+
|
|
470
518
|
|
|
471
519
|
class Panel(Component):
|
|
472
520
|
"""Base class for defining an individual display panel within a Layout within a Tab."""
|
|
@@ -606,7 +654,7 @@ class DataTablePanel(Panel):
|
|
|
606
654
|
**kwargs,
|
|
607
655
|
):
|
|
608
656
|
"""
|
|
609
|
-
Instantiate a
|
|
657
|
+
Instantiate a DataTablePanel.
|
|
610
658
|
|
|
611
659
|
Args:
|
|
612
660
|
context_data_key (str): The key in the render context that stores the data used to populate the table.
|
|
@@ -683,6 +731,7 @@ class ObjectsTablePanel(Panel):
|
|
|
683
731
|
table_class=None,
|
|
684
732
|
table_filter=None,
|
|
685
733
|
table_attribute=None,
|
|
734
|
+
distinct=False,
|
|
686
735
|
select_related_fields=None,
|
|
687
736
|
prefetch_related_fields=None,
|
|
688
737
|
order_by_fields=None,
|
|
@@ -725,6 +774,7 @@ class ObjectsTablePanel(Panel):
|
|
|
725
774
|
table_attribute (str, optional): The attribute of the detail view instance that contains the queryset to
|
|
726
775
|
initialize the table class. e.g. `dynamic_groups`.
|
|
727
776
|
Mutually exclusive with `table_filter`.
|
|
777
|
+
distinct (bool, optional): If True, apply `.distinct()` to the table queryset.
|
|
728
778
|
select_related_fields (list, optional): list of fields to pass to table queryset's `select_related` method.
|
|
729
779
|
prefetch_related_fields (list, optional): list of fields to pass to table queryset's `prefetch_related`
|
|
730
780
|
method.
|
|
@@ -760,6 +810,7 @@ class ObjectsTablePanel(Panel):
|
|
|
760
810
|
table_class,
|
|
761
811
|
table_filter,
|
|
762
812
|
table_attribute,
|
|
813
|
+
distinct,
|
|
763
814
|
select_related_fields,
|
|
764
815
|
prefetch_related_fields,
|
|
765
816
|
order_by_fields,
|
|
@@ -768,8 +819,8 @@ class ObjectsTablePanel(Panel):
|
|
|
768
819
|
):
|
|
769
820
|
raise ValueError(
|
|
770
821
|
"context_table_key cannot be combined with any of the args that are used to dynamically construct the "
|
|
771
|
-
"table (table_class, table_filter, table_attribute,
|
|
772
|
-
"order_by_fields, hide_hierarchy_ui)."
|
|
822
|
+
"table (table_class, table_filter, table_attribute, distinct, select_related_fields, "
|
|
823
|
+
"prefetch_related_fields, order_by_fields, hide_hierarchy_ui)."
|
|
773
824
|
)
|
|
774
825
|
self.context_table_key = context_table_key
|
|
775
826
|
self.table_class = table_class
|
|
@@ -781,6 +832,7 @@ class ObjectsTablePanel(Panel):
|
|
|
781
832
|
raise ValueError("You must provide a `related_field_name` when specifying `table_attribute`")
|
|
782
833
|
self.table_filter = table_filter
|
|
783
834
|
self.table_attribute = table_attribute
|
|
835
|
+
self.distinct = distinct
|
|
784
836
|
self.select_related_fields = select_related_fields
|
|
785
837
|
self.prefetch_related_fields = prefetch_related_fields
|
|
786
838
|
self.order_by_fields = order_by_fields
|
|
@@ -791,7 +843,7 @@ class ObjectsTablePanel(Panel):
|
|
|
791
843
|
self.include_columns = include_columns
|
|
792
844
|
self.exclude_columns = exclude_columns
|
|
793
845
|
self.add_button_route = add_button_route
|
|
794
|
-
self.add_permissions = add_permissions
|
|
846
|
+
self.add_permissions = add_permissions or []
|
|
795
847
|
self.hide_hierarchy_ui = hide_hierarchy_ui
|
|
796
848
|
self.related_field_name = related_field_name
|
|
797
849
|
self.enable_bulk_actions = enable_bulk_actions
|
|
@@ -822,21 +874,26 @@ class ObjectsTablePanel(Panel):
|
|
|
822
874
|
if self.tab_id:
|
|
823
875
|
return_url += f"?tab={self.tab_id}"
|
|
824
876
|
|
|
825
|
-
if self.add_button_route
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
877
|
+
if self.add_button_route is not None:
|
|
878
|
+
add_permissions = self.add_permissions
|
|
879
|
+
if not add_permissions:
|
|
880
|
+
body_content_table_class = self.table_class or context[self.context_table_key].__class__
|
|
881
|
+
body_content_table_model = body_content_table_class.Meta.model
|
|
882
|
+
add_permissions = [get_permission_for_model(body_content_table_model, "add")]
|
|
883
|
+
|
|
884
|
+
if self.add_button_route == "default":
|
|
885
|
+
if request.user.has_perms(add_permissions):
|
|
886
|
+
try:
|
|
887
|
+
add_route = reverse(get_route_for_model(body_content_table_model, "add"))
|
|
888
|
+
body_content_table_add_url = (
|
|
889
|
+
f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
|
|
890
|
+
)
|
|
891
|
+
except NoReverseMatch:
|
|
892
|
+
logger.warning("add route for `body_content_table_model` not found")
|
|
893
|
+
else:
|
|
894
|
+
if request.user.has_perms(add_permissions):
|
|
895
|
+
add_route = reverse(self.add_button_route)
|
|
832
896
|
body_content_table_add_url = f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
|
|
833
|
-
except NoReverseMatch:
|
|
834
|
-
logger.warning("add route for `body_content_table_model` not found")
|
|
835
|
-
|
|
836
|
-
elif self.add_button_route is not None:
|
|
837
|
-
if request.user.has_perms(self.add_permissions or []):
|
|
838
|
-
add_route = reverse(self.add_button_route)
|
|
839
|
-
body_content_table_add_url = f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
|
|
840
897
|
|
|
841
898
|
return body_content_table_add_url
|
|
842
899
|
|
|
@@ -879,7 +936,8 @@ class ObjectsTablePanel(Panel):
|
|
|
879
936
|
)
|
|
880
937
|
if self.order_by_fields:
|
|
881
938
|
body_content_table_queryset = body_content_table_queryset.order_by(*self.order_by_fields)
|
|
882
|
-
|
|
939
|
+
if self.distinct:
|
|
940
|
+
body_content_table_queryset = body_content_table_queryset.distinct()
|
|
883
941
|
body_content_table = body_content_table_class(
|
|
884
942
|
body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui, user=request.user
|
|
885
943
|
)
|
|
@@ -971,6 +1029,7 @@ class KeyValueTablePanel(Panel):
|
|
|
971
1029
|
context_data_key=None,
|
|
972
1030
|
hide_if_unset=(),
|
|
973
1031
|
value_transforms=None,
|
|
1032
|
+
key_transforms=None,
|
|
974
1033
|
body_wrapper_template_path="components/panel/body_wrapper_key_value_table.html",
|
|
975
1034
|
**kwargs,
|
|
976
1035
|
):
|
|
@@ -989,6 +1048,8 @@ class KeyValueTablePanel(Panel):
|
|
|
989
1048
|
|
|
990
1049
|
- `[render_markdown, placeholder]` - render the given text as Markdown, or render a placeholder if blank
|
|
991
1050
|
- `[humanize_speed, placeholder]` - convert the given kbps value to Mbps or Gbps for display
|
|
1051
|
+
key_transforms (dict, optional): A mapping of original field names to custom display names to be used when rendering keys
|
|
1052
|
+
For example: {'content_types': 'Content Type'}.
|
|
992
1053
|
"""
|
|
993
1054
|
if data and context_data_key:
|
|
994
1055
|
raise ValueError("The data and context_data_key parameters are mutually exclusive")
|
|
@@ -996,9 +1057,12 @@ class KeyValueTablePanel(Panel):
|
|
|
996
1057
|
self.context_data_key = context_data_key or "data"
|
|
997
1058
|
self.hide_if_unset = hide_if_unset
|
|
998
1059
|
self.value_transforms = value_transforms or {}
|
|
1060
|
+
self.key_transforms = key_transforms or {}
|
|
999
1061
|
super().__init__(body_wrapper_template_path=body_wrapper_template_path, **kwargs)
|
|
1000
1062
|
|
|
1001
1063
|
def should_render(self, context: Context):
|
|
1064
|
+
if not super().should_render(context):
|
|
1065
|
+
return False
|
|
1002
1066
|
return bool(self.get_data(context))
|
|
1003
1067
|
|
|
1004
1068
|
def get_data(self, context: Context):
|
|
@@ -1088,6 +1152,10 @@ class KeyValueTablePanel(Panel):
|
|
|
1088
1152
|
elif isinstance(value, models.Model):
|
|
1089
1153
|
if hasattr(value, "color"):
|
|
1090
1154
|
display = hyperlinked_object_with_color(value)
|
|
1155
|
+
elif isinstance(value, Cluster) and value.cluster_group is not None:
|
|
1156
|
+
display = format_html("{} / {}", hyperlinked_object(value.cluster_group), hyperlinked_object(value))
|
|
1157
|
+
elif isinstance(value, Rack) and value.rack_group is not None:
|
|
1158
|
+
display = format_html("{} / {}", hyperlinked_object(value.rack_group), hyperlinked_object(value))
|
|
1091
1159
|
elif isinstance(value, Tenant) and value.tenant_group is not None:
|
|
1092
1160
|
display = format_html("{} / {}", hyperlinked_object(value.tenant_group), hyperlinked_object(value))
|
|
1093
1161
|
# TODO: render location hierarchy for Location objects
|
|
@@ -1176,6 +1244,7 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1176
1244
|
self,
|
|
1177
1245
|
*,
|
|
1178
1246
|
fields="__all__",
|
|
1247
|
+
additional_fields=(),
|
|
1179
1248
|
exclude_fields=(),
|
|
1180
1249
|
context_object_key=None,
|
|
1181
1250
|
ignore_nonexistent_fields=False,
|
|
@@ -1189,6 +1258,11 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1189
1258
|
fields (str, list): The ordered list of fields to display, or `"__all__"` to display fields automatically.
|
|
1190
1259
|
Note that ManyToMany fields and reverse relations are **not** included in `"__all__"` at this time, nor
|
|
1191
1260
|
are any hidden fields, nor the specially handled `id`, `created`, `last_updated` fields on most models.
|
|
1261
|
+
When a list is specified, it may include model `@property` attributes and nested lookups (if desired) in
|
|
1262
|
+
addition to concrete model fields; when using `fields="__all__"`, such additional attributes and lookups
|
|
1263
|
+
may be specified with the `additional_fields` parameter.
|
|
1264
|
+
additional_fields (list): Only relevant if `fields == "__all__"`, in which case it can specify additional
|
|
1265
|
+
non-default fields to include such as reverse relations, `@property` attributes, nested lookups, etc.
|
|
1192
1266
|
exclude_fields (list): Only relevant if `fields == "__all__"`, in which case it excludes the given fields.
|
|
1193
1267
|
context_object_key (str): The key in the render context that will contain the object to derive fields from.
|
|
1194
1268
|
ignore_nonexistent_fields (bool): If True, `fields` is permitted to include field names that don't actually
|
|
@@ -1197,6 +1271,11 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1197
1271
|
(see `render_label()`).
|
|
1198
1272
|
"""
|
|
1199
1273
|
self.fields = fields
|
|
1274
|
+
if additional_fields and fields != "__all__":
|
|
1275
|
+
raise ValueError("additional_fields may only be used in combination with fields='__all__'")
|
|
1276
|
+
self.additional_fields = additional_fields
|
|
1277
|
+
if exclude_fields and fields != "__all__":
|
|
1278
|
+
raise ValueError("exclude_fields may only be used in combination with fields='__all__'")
|
|
1200
1279
|
self.exclude_fields = exclude_fields
|
|
1201
1280
|
self.context_object_key = context_object_key
|
|
1202
1281
|
self.ignore_nonexistent_fields = ignore_nonexistent_fields
|
|
@@ -1210,6 +1289,7 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1210
1289
|
|
|
1211
1290
|
def render_value(self, key, value, context: Context):
|
|
1212
1291
|
obj = get_obj_from_context(context, self.context_object_key)
|
|
1292
|
+
# TODO: handle nested keys, e.g. device_type__device_family
|
|
1213
1293
|
try:
|
|
1214
1294
|
field_instance = obj._meta.get_field(key)
|
|
1215
1295
|
except FieldDoesNotExist:
|
|
@@ -1230,8 +1310,18 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1230
1310
|
if isinstance(field_instance, JSONField):
|
|
1231
1311
|
return format_html("<pre>{}</pre>", render_json(value))
|
|
1232
1312
|
|
|
1233
|
-
if isinstance(field_instance, ManyToManyField)
|
|
1234
|
-
|
|
1313
|
+
if isinstance(field_instance, ManyToManyField):
|
|
1314
|
+
if field_instance.related_model == ContentType:
|
|
1315
|
+
return render_content_types(value)
|
|
1316
|
+
# TODO: this would be nice but it's probably too error-prone in general:
|
|
1317
|
+
# return render_m2m(
|
|
1318
|
+
# value.all(),
|
|
1319
|
+
# (
|
|
1320
|
+
# reverse(get_route_for_model(field_instance.related_model, "list")) + "?" +
|
|
1321
|
+
# obj._meta.verbose_name_plural.lower().replace(" ", "_") + "=" + str(obj.pk)
|
|
1322
|
+
# ),
|
|
1323
|
+
# key,
|
|
1324
|
+
# )
|
|
1235
1325
|
|
|
1236
1326
|
if isinstance(field_instance, CharField) and hasattr(obj, f"get_{key}_display"):
|
|
1237
1327
|
# For example, Secret.provider -> Secret.get_provider_display()
|
|
@@ -1273,6 +1363,8 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1273
1363
|
# TODO: apply a default ordering "smarter" than declaration order? Alphabetical? By field type?
|
|
1274
1364
|
# TODO: allow model to specify an alternative field ordering?
|
|
1275
1365
|
|
|
1366
|
+
fields += self.additional_fields
|
|
1367
|
+
|
|
1276
1368
|
data = {}
|
|
1277
1369
|
|
|
1278
1370
|
if isinstance(instance, TreeModel) and (self.fields == "__all__" or "_hierarchy" in self.fields):
|
|
@@ -1283,9 +1375,16 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1283
1375
|
if field_name in self.exclude_fields:
|
|
1284
1376
|
continue
|
|
1285
1377
|
try:
|
|
1286
|
-
field_value =
|
|
1287
|
-
|
|
1288
|
-
|
|
1378
|
+
field_value = instance
|
|
1379
|
+
# Handle nested lookups, e.g. device_type__device_family
|
|
1380
|
+
for token in field_name.split(LOOKUP_SEP):
|
|
1381
|
+
try:
|
|
1382
|
+
field_value = getattr(field_value, token)
|
|
1383
|
+
if field_value is None:
|
|
1384
|
+
break
|
|
1385
|
+
except ObjectDoesNotExist:
|
|
1386
|
+
field_value = None
|
|
1387
|
+
break
|
|
1289
1388
|
except AttributeError:
|
|
1290
1389
|
if self.ignore_nonexistent_fields:
|
|
1291
1390
|
continue
|
|
@@ -1301,6 +1400,10 @@ class ObjectFieldsPanel(KeyValueTablePanel):
|
|
|
1301
1400
|
|
|
1302
1401
|
def render_key(self, key, value, context: Context):
|
|
1303
1402
|
"""Render the `verbose_name` of the model field whose name corresponds to the given key, if applicable."""
|
|
1403
|
+
|
|
1404
|
+
if key in self.key_transforms:
|
|
1405
|
+
return self.key_transforms[key]
|
|
1406
|
+
|
|
1304
1407
|
instance = get_obj_from_context(context, self.context_object_key)
|
|
1305
1408
|
|
|
1306
1409
|
if instance is not None:
|
|
@@ -1572,7 +1675,7 @@ class _ObjectCustomFieldsPanel(GroupedKeyValueTablePanel):
|
|
|
1572
1675
|
if not hasattr(obj, "get_custom_field_groupings"):
|
|
1573
1676
|
return False
|
|
1574
1677
|
self.custom_field_data = obj.get_custom_field_groupings(advanced_ui=self.advanced_ui)
|
|
1575
|
-
return bool(self.custom_field_data)
|
|
1678
|
+
return bool(self.custom_field_data) and super().should_render(context)
|
|
1576
1679
|
|
|
1577
1680
|
def get_data(self, context: Context):
|
|
1578
1681
|
"""Remap the response from `get_custom_field_groupings()` to a nested dict as expected by the parent class."""
|
|
@@ -1648,7 +1751,7 @@ class _ObjectComputedFieldsPanel(GroupedKeyValueTablePanel):
|
|
|
1648
1751
|
if not hasattr(obj, "get_computed_fields_grouping"):
|
|
1649
1752
|
return False
|
|
1650
1753
|
self.computed_fields_data = obj.get_computed_fields_grouping(advanced_ui=self.advanced_ui)
|
|
1651
|
-
return bool(self.computed_fields_data)
|
|
1754
|
+
return bool(self.computed_fields_data) and super().should_render(context)
|
|
1652
1755
|
|
|
1653
1756
|
def get_data(self, context: Context):
|
|
1654
1757
|
"""Remap `get_computed_fields_grouping()` to the nested dict format expected by the base class."""
|
|
@@ -1694,7 +1797,7 @@ class _ObjectRelationshipsPanel(KeyValueTablePanel):
|
|
|
1694
1797
|
self.relationships_data["source"]
|
|
1695
1798
|
or self.relationships_data["destination"]
|
|
1696
1799
|
or self.relationships_data["peer"]
|
|
1697
|
-
)
|
|
1800
|
+
) and super().should_render(context)
|
|
1698
1801
|
|
|
1699
1802
|
def get_data(self, context: Context):
|
|
1700
1803
|
"""Remap `get_relationships_with_related_objects()` to the flat dict format expected by the base class."""
|
|
@@ -1745,6 +1848,8 @@ class _ObjectTagsPanel(Panel):
|
|
|
1745
1848
|
)
|
|
1746
1849
|
|
|
1747
1850
|
def should_render(self, context: Context):
|
|
1851
|
+
if not super().should_render(context):
|
|
1852
|
+
return False
|
|
1748
1853
|
return hasattr(get_obj_from_context(context), "tags")
|
|
1749
1854
|
|
|
1750
1855
|
def get_extra_context(self, context: Context):
|
|
@@ -1776,6 +1881,8 @@ class _ObjectCommentPanel(ObjectTextPanel):
|
|
|
1776
1881
|
)
|
|
1777
1882
|
|
|
1778
1883
|
def should_render(self, context: Context):
|
|
1884
|
+
if not super().should_render(context):
|
|
1885
|
+
return False
|
|
1779
1886
|
return hasattr(get_obj_from_context(context), "comments")
|
|
1780
1887
|
|
|
1781
1888
|
|
|
@@ -1909,6 +2016,8 @@ class _ObjectDetailContactsTab(Tab):
|
|
|
1909
2016
|
super().__init__(tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
|
|
1910
2017
|
|
|
1911
2018
|
def should_render(self, context: Context):
|
|
2019
|
+
if not super().should_render(context):
|
|
2020
|
+
return False
|
|
1912
2021
|
return getattr(get_obj_from_context(context), "is_contact_associable_model", False)
|
|
1913
2022
|
|
|
1914
2023
|
def render_label(self, context: Context):
|
|
@@ -1933,6 +2042,7 @@ class _ObjectDetailGroupsTab(Tab):
|
|
|
1933
2042
|
label="Dynamic Groups",
|
|
1934
2043
|
weight=Tab.WEIGHT_GROUPS_TAB,
|
|
1935
2044
|
panels=None,
|
|
2045
|
+
required_permissions=("extras.view_dynamic_group",),
|
|
1936
2046
|
**kwargs,
|
|
1937
2047
|
):
|
|
1938
2048
|
if panels is None:
|
|
@@ -1946,15 +2056,20 @@ class _ObjectDetailGroupsTab(Tab):
|
|
|
1946
2056
|
related_field_name="member_id",
|
|
1947
2057
|
),
|
|
1948
2058
|
)
|
|
1949
|
-
super().__init__(
|
|
2059
|
+
super().__init__(
|
|
2060
|
+
tab_id=tab_id,
|
|
2061
|
+
label=label,
|
|
2062
|
+
weight=weight,
|
|
2063
|
+
panels=panels,
|
|
2064
|
+
required_permissions=required_permissions,
|
|
2065
|
+
**kwargs,
|
|
2066
|
+
)
|
|
1950
2067
|
|
|
1951
2068
|
def should_render(self, context: Context):
|
|
2069
|
+
if not super().should_render(context):
|
|
2070
|
+
return False
|
|
1952
2071
|
obj = get_obj_from_context(context)
|
|
1953
|
-
return (
|
|
1954
|
-
getattr(obj, "is_dynamic_group_associable_model", False)
|
|
1955
|
-
and context["request"].user.has_perm("extras.view_dynamicgroup")
|
|
1956
|
-
and obj.dynamic_groups.exists()
|
|
1957
|
-
)
|
|
2072
|
+
return getattr(obj, "is_dynamic_group_associable_model", False) and obj.dynamic_groups.exists()
|
|
1958
2073
|
|
|
1959
2074
|
def render_label(self, context: Context):
|
|
1960
2075
|
return format_html(
|
|
@@ -1977,6 +2092,7 @@ class _ObjectDetailMetadataTab(Tab):
|
|
|
1977
2092
|
label="Object Metadata",
|
|
1978
2093
|
weight=Tab.WEIGHT_METADATA_TAB,
|
|
1979
2094
|
panels=None,
|
|
2095
|
+
required_permissions=("extras.view_objectmetadata",),
|
|
1980
2096
|
**kwargs,
|
|
1981
2097
|
):
|
|
1982
2098
|
if panels is None:
|
|
@@ -1992,15 +2108,20 @@ class _ObjectDetailMetadataTab(Tab):
|
|
|
1992
2108
|
header_extra_content_template_path=None,
|
|
1993
2109
|
),
|
|
1994
2110
|
)
|
|
1995
|
-
super().__init__(
|
|
2111
|
+
super().__init__(
|
|
2112
|
+
tab_id=tab_id,
|
|
2113
|
+
label=label,
|
|
2114
|
+
weight=weight,
|
|
2115
|
+
panels=panels,
|
|
2116
|
+
required_permissions=required_permissions,
|
|
2117
|
+
**kwargs,
|
|
2118
|
+
)
|
|
1996
2119
|
|
|
1997
2120
|
def should_render(self, context: Context):
|
|
2121
|
+
if not super().should_render(context):
|
|
2122
|
+
return False
|
|
1998
2123
|
obj = get_obj_from_context(context)
|
|
1999
|
-
return (
|
|
2000
|
-
getattr(obj, "is_metadata_associable_model", False)
|
|
2001
|
-
and context["request"].user.has_perm("extras.view_objectmetadata")
|
|
2002
|
-
and obj.associated_object_metadata.exists()
|
|
2003
|
-
)
|
|
2124
|
+
return getattr(obj, "is_metadata_associable_model", False) and obj.associated_object_metadata.exists()
|
|
2004
2125
|
|
|
2005
2126
|
def render_label(self, context: Context):
|
|
2006
2127
|
return format_html(
|
nautobot/core/ui/titles.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from typing import Literal, Optional
|
|
1
|
+
from typing import Literal, Optional, Union
|
|
2
2
|
|
|
3
3
|
from django.template import Context, Template
|
|
4
4
|
from django.utils.html import strip_tags
|
|
5
5
|
|
|
6
6
|
DEFAULT_TITLES: dict[str, str] = {
|
|
7
|
+
"*": "{{ verbose_name_plural|bettertitle }}",
|
|
7
8
|
"list": "{{ verbose_name_plural|bettertitle }}",
|
|
8
9
|
"detail": "{{ object.display|default:object }}",
|
|
9
10
|
"retrieve": "{{ object.display|default:object }}",
|
|
@@ -14,6 +15,7 @@ DEFAULT_TITLES: dict[str, str] = {
|
|
|
14
15
|
"bulk_rename": "Renaming {{ selected_objects|length }} {{ verbose_name_plural|bettertitle }} on {{ parent_name }}",
|
|
15
16
|
"bulk_update": "Editing {{ objs_count }} {{ verbose_name_plural|bettertitle }}",
|
|
16
17
|
"changelog": "{{ object.display|default:object }} - Change Log",
|
|
18
|
+
"config_context": "{{ object.display|default:object }} - Config Context",
|
|
17
19
|
"notes": "{{ object.display|default:object }} - Notes",
|
|
18
20
|
"approve": "Approve {{ verbose_name|bettertitle }}?",
|
|
19
21
|
"deny": "Deny {{ verbose_name|bettertitle }}?",
|
|
@@ -59,7 +61,7 @@ class Titles:
|
|
|
59
61
|
if template_plugins:
|
|
60
62
|
self.template_plugins.extend(template_plugins)
|
|
61
63
|
|
|
62
|
-
def render(self, context: Context, mode: ModeType = "html") -> str:
|
|
64
|
+
def render(self, context: Union[dict, Context], mode: ModeType = "html") -> str:
|
|
63
65
|
"""
|
|
64
66
|
Renders the title based on given context and current action.
|
|
65
67
|
|
|
@@ -68,12 +70,15 @@ class Titles:
|
|
|
68
70
|
Make sure that needed context variables are in context and needed plugins are loaded.
|
|
69
71
|
|
|
70
72
|
Args:
|
|
71
|
-
context (Context): Render context.
|
|
73
|
+
context (Union[dict, Context]): Render context.
|
|
72
74
|
mode (ModeType): Rendering mode: "html" or "plain".
|
|
73
75
|
|
|
74
76
|
Returns:
|
|
75
77
|
(str): HTML fragment or plain text, depending on `mode`.
|
|
76
78
|
"""
|
|
79
|
+
if isinstance(context, dict):
|
|
80
|
+
context = Context(context)
|
|
81
|
+
|
|
77
82
|
with context.update(self.get_extra_context(context)):
|
|
78
83
|
template_str = self.get_template_str(context)
|
|
79
84
|
template = Template(self.template_plugins_str + template_str)
|
|
@@ -92,7 +97,7 @@ class Titles:
|
|
|
92
97
|
Returns:
|
|
93
98
|
str: The template string for the current action, or an empty string if not found.
|
|
94
99
|
"""
|
|
95
|
-
action = context.get("view_action", "
|
|
100
|
+
action = context.get("view_action", "")
|
|
96
101
|
|
|
97
102
|
template_str = self.titles.get(action)
|
|
98
103
|
if template_str:
|
|
@@ -102,7 +107,7 @@ class Titles:
|
|
|
102
107
|
if detail:
|
|
103
108
|
return self.titles.get("detail", "")
|
|
104
109
|
|
|
105
|
-
return ""
|
|
110
|
+
return self.titles.get("*", "")
|
|
106
111
|
|
|
107
112
|
@property
|
|
108
113
|
def template_plugins_str(self) -> str:
|