nautobot 2.4.21__py3-none-any.whl → 3.0.0a3__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/choices.py +2 -2
- nautobot/apps/filters.py +9 -9
- nautobot/apps/forms.py +2 -0
- nautobot/apps/models.py +7 -2
- nautobot/apps/ui.py +20 -1
- nautobot/apps/utils.py +2 -3
- nautobot/apps/views.py +7 -1
- nautobot/circuits/filters.py +8 -23
- nautobot/circuits/navigation.py +3 -1
- nautobot/circuits/templates/circuits/circuit_create.html +9 -9
- nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
- nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
- nautobot/circuits/templates/circuits/provider_create.html +9 -9
- nautobot/circuits/tests/integration/test_circuit.py +19 -19
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
- nautobot/circuits/tests/integration/test_relationships.py +4 -12
- nautobot/circuits/views.py +0 -2
- nautobot/cloud/filters.py +1 -13
- nautobot/cloud/navigation.py +3 -1
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
- nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
- nautobot/core/api/fields.py +30 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +9 -2
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +58 -37
- nautobot/core/apps/__init__.py +6 -12
- nautobot/core/branching.py +83 -0
- nautobot/core/celery/__init__.py +11 -6
- nautobot/core/celery/backends.py +2 -0
- nautobot/core/celery/encoders.py +7 -0
- nautobot/core/celery/task.py +44 -0
- nautobot/core/checks.py +60 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
- nautobot/core/constants.py +9 -0
- nautobot/core/context_processors.py +84 -0
- nautobot/core/filters.py +131 -2
- nautobot/core/forms/__init__.py +4 -2
- nautobot/core/forms/fields.py +10 -8
- nautobot/core/forms/forms.py +21 -9
- nautobot/core/forms/search.py +0 -15
- nautobot/core/forms/widgets.py +3 -2
- nautobot/core/graphql/__init__.py +8 -26
- nautobot/core/graphql/generators.py +16 -6
- nautobot/core/graphql/schema.py +1 -1
- nautobot/core/graphql/schema_init.py +1 -2
- nautobot/core/graphql/utils.py +7 -9
- nautobot/core/jobs/__init__.py +158 -0
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +17 -2
- nautobot/core/models/fields.py +3 -2
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/name_color_content_types.py +1 -1
- nautobot/core/models/ordering.py +7 -5
- nautobot/core/models/querysets.py +77 -2
- nautobot/core/models/tree_queries.py +6 -4
- nautobot/core/settings.py +30 -16
- nautobot/core/settings.yaml +13 -7
- nautobot/core/tables.py +114 -44
- nautobot/core/templates/403.html +1 -1
- nautobot/core/templates/403_csrf_failure.html +1 -1
- nautobot/core/templates/404.html +1 -1
- nautobot/core/templates/40x.html +8 -8
- nautobot/core/templates/500.html +10 -10
- nautobot/core/templates/about.html +13 -12
- nautobot/core/templates/admin/actions.html +1 -1
- nautobot/core/templates/admin/app_index.html +3 -3
- nautobot/core/templates/admin/base.html +45 -52
- nautobot/core/templates/admin/base_site.html +0 -9
- nautobot/core/templates/admin/change_form.html +5 -5
- nautobot/core/templates/admin/change_list.html +8 -12
- nautobot/core/templates/admin/change_list_results.html +3 -3
- nautobot/core/templates/admin/config/config.html +24 -24
- nautobot/core/templates/admin/delete_confirmation.html +5 -5
- nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
- nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
- nautobot/core/templates/admin/includes/fieldset.html +15 -15
- nautobot/core/templates/admin/index.html +8 -8
- nautobot/core/templates/admin/submit_line.html +5 -5
- nautobot/core/templates/base_django.html +36 -32
- nautobot/core/templates/buttons/add.html +1 -1
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
- nautobot/core/templates/buttons/export.html +17 -18
- nautobot/core/templates/buttons/job_import.html +2 -2
- nautobot/core/templates/components/breadcrumbs.html +19 -17
- nautobot/core/templates/components/button/dropdown.html +7 -5
- nautobot/core/templates/components/echarts.html +2 -0
- nautobot/core/templates/components/layout/one_over_two.html +3 -3
- nautobot/core/templates/components/layout/two_over_one.html +3 -3
- nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
- nautobot/core/templates/components/panel/body_content_tags.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
- nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
- nautobot/core/templates/components/panel/footer_content_table.html +3 -3
- nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
- nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
- nautobot/core/templates/components/panel/panel.html +6 -3
- nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
- nautobot/core/templates/components/tab/content_wrapper.html +29 -1
- nautobot/core/templates/components/tab/label_wrapper.html +10 -2
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
- nautobot/core/templates/echarts/echarts.html +20 -0
- nautobot/core/templates/exceptions/import_error.html +2 -2
- nautobot/core/templates/exceptions/permission_error.html +1 -1
- nautobot/core/templates/exceptions/programming_error.html +2 -2
- nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
- nautobot/core/templates/generic/object_bulk_create.html +87 -75
- nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
- nautobot/core/templates/generic/object_bulk_remove.html +30 -26
- nautobot/core/templates/generic/object_bulk_rename.html +53 -40
- nautobot/core/templates/generic/object_bulk_update.html +36 -29
- nautobot/core/templates/generic/object_create.html +40 -27
- nautobot/core/templates/generic/object_import.html +36 -24
- nautobot/core/templates/generic/object_list.html +279 -215
- nautobot/core/templates/generic/object_notes.html +21 -11
- nautobot/core/templates/generic/object_retrieve.html +161 -213
- nautobot/core/templates/graphene/graphiql.html +113 -60
- nautobot/core/templates/home.html +164 -87
- nautobot/core/templates/import_success.html +3 -2
- nautobot/core/templates/inc/ajax_loader.html +1 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
- nautobot/core/templates/inc/created_updated.html +12 -7
- nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
- nautobot/core/templates/inc/custom_fields_panel.html +3 -3
- nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
- nautobot/core/templates/inc/footer.html +90 -40
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +75 -0
- nautobot/core/templates/inc/header_banners.html +17 -0
- nautobot/core/templates/inc/header_messages.html +6 -0
- nautobot/core/templates/inc/image_attachments.html +9 -9
- nautobot/core/templates/inc/javascript.html +7 -24
- nautobot/core/templates/inc/media.html +4 -29
- nautobot/core/templates/inc/modal.html +2 -2
- nautobot/core/templates/inc/nav_favorites.html +27 -0
- nautobot/core/templates/inc/nav_menu.html +150 -108
- nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
- nautobot/core/templates/inc/page_title.html +23 -0
- nautobot/core/templates/inc/paginator.html +39 -28
- nautobot/core/templates/inc/relationships/panel_override.html +3 -3
- nautobot/core/templates/inc/relationships_panel.html +3 -3
- nautobot/core/templates/inc/relationships_table_rows.html +1 -1
- nautobot/core/templates/inc/search_panel.html +22 -16
- nautobot/core/templates/inc/table.html +61 -36
- nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
- nautobot/core/templates/login.html +17 -59
- nautobot/core/templates/modals/modal_theme.html +12 -23
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/panel_table.html +8 -12
- nautobot/core/templates/redoc_ui.html +80 -0
- nautobot/core/templates/rest_framework/api.html +43 -21
- nautobot/core/templates/search.html +12 -13
- nautobot/core/templates/swagger_ui.html +19 -4
- nautobot/core/templates/system_jobs/import_objects.html +70 -58
- nautobot/core/templates/template.css +0 -6
- nautobot/core/templates/utilities/comment_form.html +34 -0
- nautobot/core/templates/utilities/confirmation_form.html +17 -9
- nautobot/core/templates/utilities/obj_table.html +19 -11
- nautobot/core/templates/utilities/render_field.html +27 -21
- nautobot/core/templates/utilities/render_jinja2.html +22 -25
- nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
- nautobot/core/templates/utilities/templatetags/badge.html +1 -1
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
- nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
- nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
- nautobot/core/templates/utilities/theme_preview.html +829 -566
- nautobot/core/templates/utilities/worker_status.html +42 -41
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templates/widgets/sluginput.html +2 -2
- nautobot/core/templatetags/buttons.py +38 -40
- nautobot/core/templatetags/helpers.py +105 -28
- nautobot/core/templatetags/ui_framework.py +17 -0
- nautobot/core/testing/api.py +76 -12
- nautobot/core/testing/filters.py +11 -27
- nautobot/core/testing/integration.py +128 -10
- nautobot/core/testing/mixins.py +7 -4
- nautobot/core/testing/utils.py +28 -5
- nautobot/core/testing/views.py +125 -27
- nautobot/core/tests/integration/test_app_home.py +39 -35
- nautobot/core/tests/integration/test_app_navbar.py +60 -67
- nautobot/core/tests/integration/test_filters.py +123 -55
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/integration/test_home.py +10 -18
- nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
- nautobot/core/tests/integration/test_navbar.py +41 -16
- nautobot/core/tests/integration/test_swagger.py +1 -7
- nautobot/core/tests/integration/test_theme.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +6 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_branching.py +154 -0
- nautobot/core/tests/test_breadcrumbs.py +7 -8
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_commands.py +0 -41
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_csv.py +4 -7
- nautobot/core/tests/test_filters.py +326 -318
- nautobot/core/tests/test_forms.py +19 -30
- nautobot/core/tests/test_graphql.py +67 -57
- nautobot/core/tests/test_models.py +1 -1
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +78 -10
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +61 -21
- nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
- nautobot/core/tests/test_ui.py +207 -2
- nautobot/core/tests/test_utils.py +147 -2
- nautobot/core/tests/test_views.py +201 -64
- nautobot/core/tests/test_views_utils.py +1 -1
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +190 -0
- nautobot/core/ui/constants.py +86 -0
- nautobot/core/ui/echarts.py +474 -0
- nautobot/core/ui/nav.py +5 -1
- nautobot/core/ui/object_detail.py +180 -16
- nautobot/core/urls.py +13 -1
- nautobot/core/utils/cache.py +71 -0
- nautobot/core/utils/data.py +8 -5
- nautobot/core/utils/filtering.py +8 -2
- nautobot/core/utils/git.py +3 -3
- nautobot/core/utils/lookup.py +87 -13
- nautobot/core/utils/migrations.py +22 -0
- nautobot/core/utils/module_loading.py +26 -0
- nautobot/core/utils/permissions.py +9 -5
- nautobot/core/views/__init__.py +114 -63
- nautobot/core/views/generic.py +34 -27
- nautobot/core/views/mixins.py +49 -27
- nautobot/core/views/renderers.py +3 -5
- nautobot/core/views/utils.py +10 -5
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/__init__.py +0 -0
- nautobot/data_validation/api/__init__.py +1 -0
- nautobot/data_validation/api/serializers.py +80 -0
- nautobot/data_validation/api/urls.py +20 -0
- nautobot/data_validation/api/views.py +44 -0
- nautobot/data_validation/apps.py +18 -0
- nautobot/data_validation/custom_validators.py +330 -0
- nautobot/data_validation/filters.py +133 -0
- nautobot/data_validation/form_mixin.py +25 -0
- nautobot/data_validation/forms.py +342 -0
- nautobot/data_validation/migrations/0001_initial.py +224 -0
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
- nautobot/data_validation/migrations/__init__.py +0 -0
- nautobot/data_validation/models.py +361 -0
- nautobot/data_validation/navigation.py +74 -0
- nautobot/data_validation/signals.py +30 -0
- nautobot/data_validation/tables.py +259 -0
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +20 -0
- nautobot/data_validation/tests/migrations/__init__.py +0 -0
- nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
- nautobot/data_validation/tests/test_api.py +238 -0
- nautobot/data_validation/tests/test_custom_validators.py +423 -0
- nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
- nautobot/data_validation/tests/test_filters.py +240 -0
- nautobot/data_validation/tests/test_form_mixin.py +115 -0
- nautobot/data_validation/tests/test_models.py +393 -0
- nautobot/data_validation/tests/test_views.py +435 -0
- nautobot/data_validation/urls.py +21 -0
- nautobot/data_validation/views.py +227 -0
- nautobot/dcim/api/serializers.py +10 -13
- nautobot/dcim/api/urls.py +2 -0
- nautobot/dcim/api/views.py +7 -0
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +16 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
- nautobot/dcim/forms.py +12 -6
- nautobot/dcim/graphql/types.py +1 -0
- nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
- nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
- nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
- nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_components.py +3 -1
- nautobot/dcim/models/devices.py +115 -51
- nautobot/dcim/navigation.py +7 -3
- nautobot/dcim/querysets.py +6 -0
- nautobot/dcim/signals.py +19 -0
- nautobot/dcim/tables/devices.py +9 -5
- nautobot/dcim/tables/template_code.py +191 -102
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/cable_connect.html +62 -146
- nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
- nautobot/dcim/templates/dcim/cable_trace.html +15 -17
- nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/consoleport.html +18 -17
- nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
- nautobot/dcim/templates/dcim/controller_create.html +12 -8
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/device/config.html +17 -19
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
- nautobot/dcim/templates/dcim/device/status.html +20 -20
- nautobot/dcim/templates/dcim/device_component_add.html +24 -15
- nautobot/dcim/templates/dcim/device_create.html +120 -120
- nautobot/dcim/templates/dcim/device_list.html +75 -12
- nautobot/dcim/templates/dcim/devicebay.html +7 -7
- nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
- nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
- nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
- nautobot/dcim/templates/dcim/frontport.html +21 -20
- nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
- nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
- nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +42 -22
- nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/interface_edit.html +26 -11
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/location_update.html +9 -9
- nautobot/dcim/templates/dcim/locationtype.html +0 -1
- nautobot/dcim/templates/dcim/module/base.html +67 -27
- nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
- nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
- nautobot/dcim/templates/dcim/module_frontports.html +13 -15
- nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
- nautobot/dcim/templates/dcim/module_list.html +59 -10
- nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
- nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
- nautobot/dcim/templates/dcim/module_powerports.html +13 -15
- nautobot/dcim/templates/dcim/module_rearports.html +13 -15
- nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
- nautobot/dcim/templates/dcim/module_update.html +15 -9
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
- nautobot/dcim/templates/dcim/platform_create.html +9 -9
- nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
- nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +17 -16
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
- nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
- nautobot/dcim/templates/dcim/rack_update.html +14 -14
- nautobot/dcim/templates/dcim/rackreservation.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
- nautobot/dcim/templates/dcim/rearport.html +19 -18
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
- nautobot/dcim/templates/dcim/trace/device.html +1 -1
- nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/trace/termination.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
- nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
- nautobot/dcim/tests/integration/test_controller.py +6 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
- nautobot/dcim/tests/integration/test_create_device.py +9 -9
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
- nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
- nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
- nautobot/dcim/tests/test_api.py +86 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +159 -110
- nautobot/dcim/tests/test_graphql.py +32 -36
- nautobot/dcim/tests/test_jobs.py +1 -1
- nautobot/dcim/tests/test_models.py +229 -1
- nautobot/dcim/tests/test_views.py +31 -20
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +77 -41
- nautobot/extras/api/serializers.py +83 -19
- nautobot/extras/api/urls.py +7 -0
- nautobot/extras/api/views.py +243 -140
- nautobot/extras/choices.py +34 -13
- nautobot/extras/constants.py +1 -1
- nautobot/extras/context_managers.py +26 -26
- nautobot/extras/datasources/git.py +22 -0
- nautobot/extras/datasources/registry.py +3 -0
- nautobot/extras/exceptions.py +5 -0
- nautobot/extras/factory.py +11 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
- nautobot/extras/{filters/__init__.py → filters.py} +203 -58
- nautobot/extras/forms/base.py +2 -1
- nautobot/extras/forms/forms.py +225 -20
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/homepage.py +21 -2
- nautobot/extras/jobs.py +2 -8
- nautobot/extras/jobs_ui.py +2 -2
- nautobot/extras/management/__init__.py +9 -0
- nautobot/extras/managers.py +31 -22
- nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
- nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
- nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/models/__init__.py +14 -3
- nautobot/extras/models/approvals.py +556 -0
- nautobot/extras/models/change_logging.py +1 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/customfields.py +57 -22
- nautobot/extras/models/datasources.py +21 -0
- nautobot/extras/models/groups.py +2 -0
- nautobot/extras/models/jobs.py +122 -39
- nautobot/extras/models/metadata.py +2 -3
- nautobot/extras/models/mixins.py +129 -1
- nautobot/extras/models/models.py +22 -14
- nautobot/extras/models/relationships.py +47 -10
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/models/tags.py +1 -1
- nautobot/extras/navigation.py +42 -15
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +1 -23
- nautobot/extras/plugins/tables.py +8 -6
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/validators.py +10 -10
- nautobot/extras/plugins/views.py +1 -5
- nautobot/extras/querysets.py +17 -21
- nautobot/extras/signals.py +23 -8
- nautobot/extras/tables.py +420 -99
- nautobot/extras/templates/extras/approval_dashboard.html +15 -0
- nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
- nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
- nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
- nautobot/extras/templates/extras/configcontext_update.html +12 -12
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
- nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield_update.html +12 -12
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
- nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
- nautobot/extras/templates/extras/externalintegration_update.html +6 -6
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +1 -1
- nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
- nautobot/extras/templates/extras/gitrepository_update.html +25 -7
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
- nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
- nautobot/extras/templates/extras/inc/job_label.html +5 -5
- nautobot/extras/templates/extras/inc/job_table.html +23 -10
- nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
- nautobot/extras/templates/extras/inc/jobresult.html +6 -6
- nautobot/extras/templates/extras/inc/json_format.html +11 -4
- nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
- nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
- nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
- nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
- nautobot/extras/templates/extras/job.html +154 -155
- nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/job_edit.html +69 -64
- nautobot/extras/templates/extras/job_list.html +37 -60
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
- nautobot/extras/templates/extras/marketplace.html +62 -71
- nautobot/extras/templates/extras/metadatatype_create.html +9 -9
- nautobot/extras/templates/extras/note.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
- nautobot/extras/templates/extras/object_configcontext.html +20 -20
- nautobot/extras/templates/extras/object_new_contact.html +6 -6
- nautobot/extras/templates/extras/object_new_team.html +6 -6
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
- nautobot/extras/templates/extras/plugin_detail.html +40 -41
- nautobot/extras/templates/extras/plugins_list.html +23 -38
- nautobot/extras/templates/extras/plugins_tiles.html +28 -28
- nautobot/extras/templates/extras/role_retrieve.html +112 -48
- nautobot/extras/templates/extras/scheduledjob.html +25 -28
- nautobot/extras/templates/extras/secret_create.html +11 -11
- nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/tag_update.html +3 -3
- nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
- nautobot/extras/templates/extras/webhook.html +12 -12
- nautobot/extras/templatetags/approvals.py +19 -0
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +5 -8
- nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
- nautobot/extras/tests/integration/test_customfields.py +33 -33
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
- nautobot/extras/tests/integration/test_jobs.py +2 -4
- nautobot/extras/tests/integration/test_notes.py +3 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +35 -27
- nautobot/extras/tests/integration/test_relationships.py +7 -11
- nautobot/extras/tests/integration/test_tagfilter.py +3 -11
- nautobot/extras/tests/test_api.py +786 -242
- nautobot/extras/tests/test_approvals.py +715 -0
- nautobot/extras/tests/test_changelog.py +18 -14
- nautobot/extras/tests/test_customfields.py +14 -13
- nautobot/extras/tests/test_datasources.py +1 -1
- nautobot/extras/tests/test_dynamicgroups.py +9 -4
- nautobot/extras/tests/test_filters.py +443 -13
- nautobot/extras/tests/test_forms.py +18 -57
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +81 -1
- nautobot/extras/tests/test_models.py +378 -47
- nautobot/extras/tests/test_plugins.py +47 -13
- nautobot/extras/tests/test_relationships.py +7 -2
- nautobot/extras/tests/test_utils.py +2 -0
- nautobot/extras/tests/test_views.py +780 -493
- nautobot/extras/urls.py +36 -12
- nautobot/extras/utils.py +58 -12
- nautobot/extras/views.py +668 -209
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +35 -71
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -1
- nautobot/ipam/querysets.py +1 -2
- nautobot/ipam/tables.py +26 -17
- nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
- nautobot/ipam/templates/ipam/inc/service.html +8 -8
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
- nautobot/ipam/templates/ipam/ipaddress.html +27 -13
- nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
- nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_create.html +9 -9
- nautobot/ipam/templates/ipam/prefix_list.html +15 -14
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_update.html +6 -6
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
- nautobot/ipam/tests/integration/test_prefixes.py +5 -13
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +20 -7
- nautobot/ipam/tests/test_filters.py +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +24 -21
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +49 -7
- nautobot/project-static/dist/css/graphql-libraries.css +655 -0
- nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
- nautobot/project-static/dist/css/materialdesignicons.css +3 -0
- nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
- nautobot/project-static/dist/css/nautobot.css +13 -0
- nautobot/project-static/dist/css/nautobot.css.map +1 -0
- nautobot/project-static/dist/js/graphql-libraries.js +3 -0
- nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
- nautobot/project-static/dist/js/libraries.js +3 -0
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
- nautobot/project-static/dist/js/libraries.js.map +1 -0
- nautobot/project-static/dist/js/materialdesignicons.js +0 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
- nautobot/project-static/dist/js/nautobot.js +2 -0
- nautobot/project-static/dist/js/nautobot.js.map +1 -0
- nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
- nautobot/project-static/img/jinja_logo.svg +21 -92
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/editor.js +4 -4
- nautobot/project-static/js/forms.js +67 -717
- nautobot/project-static/js/job_result.js +2 -2
- nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
- nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/atom.svg +3 -0
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/branch.svg +3 -0
- nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
- nautobot/project-static/nautobot-icons/cast.svg +3 -0
- nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +3 -0
- nautobot/project-static/nautobot-icons/compass.svg +3 -0
- nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
- nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
- nautobot/project-static/nautobot-icons/direction.svg +3 -0
- nautobot/project-static/nautobot-icons/elements.svg +3 -0
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
- nautobot/project-static/nautobot-icons/globe.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +3 -0
- nautobot/project-static/nautobot-icons/history.svg +3 -0
- nautobot/project-static/nautobot-icons/ip.svg +3 -0
- nautobot/project-static/nautobot-icons/laptop.svg +3 -0
- nautobot/project-static/nautobot-icons/lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
- nautobot/project-static/nautobot-icons/map-view.svg +3 -0
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
- nautobot/project-static/nautobot-icons/plug.svg +3 -0
- nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
- nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/route.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +3 -0
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server-2.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +3 -0
- nautobot/project-static/nautobot-icons/share.svg +3 -0
- nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
- nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
- nautobot/project-static/nautobot-icons/star.svg +3 -0
- nautobot/project-static/nautobot-icons/transform.svg +3 -0
- nautobot/project-static/nautobot-icons/wifi.svg +3 -0
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/templates/tenancy/tenant_create.html +6 -6
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/.gitignore +137 -0
- nautobot/ui/.node-version +1 -0
- nautobot/ui/.prettierignore +3 -0
- nautobot/ui/eslint.config.js +33 -0
- nautobot/ui/package-lock.json +6594 -0
- nautobot/ui/package.json +67 -0
- nautobot/ui/prettier.config.js +9 -0
- nautobot/ui/src/js/collapse.js +69 -0
- nautobot/ui/src/js/cookie.js +31 -0
- nautobot/ui/src/js/draggable.js +101 -0
- nautobot/ui/src/js/drawer.js +106 -0
- nautobot/ui/src/js/form.js +23 -0
- nautobot/ui/src/js/history.js +51 -0
- nautobot/ui/src/js/nautobot-graphiql.js +19 -0
- nautobot/ui/src/js/nautobot.js +128 -0
- nautobot/ui/src/js/search.js +274 -0
- nautobot/ui/src/js/select2.js +318 -0
- nautobot/ui/src/js/sidenav.js +87 -0
- nautobot/ui/src/js/tabs.js +139 -0
- nautobot/ui/src/js/theme.js +104 -0
- nautobot/ui/src/js/utils.js +54 -0
- nautobot/ui/src/scss/colors.scss +58 -0
- nautobot/ui/src/scss/nautobot.scss +2471 -0
- nautobot/ui/webpack.config.js +148 -0
- nautobot/users/apps.py +3 -0
- nautobot/users/filters.py +7 -11
- nautobot/users/forms.py +10 -0
- nautobot/users/models.py +8 -0
- nautobot/users/templates/users/advanced_settings_edit.html +31 -21
- nautobot/users/templates/users/api_tokens.html +61 -51
- nautobot/users/templates/users/base.html +23 -31
- nautobot/users/templates/users/change_password.html +29 -19
- nautobot/users/templates/users/preferences.html +55 -45
- nautobot/users/templates/users/profile.html +45 -14
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/urls.py +2 -0
- nautobot/users/views.py +70 -2
- nautobot/virtualization/api/views.py +1 -1
- nautobot/virtualization/filters.py +18 -32
- nautobot/virtualization/forms.py +22 -59
- nautobot/virtualization/models.py +1 -19
- nautobot/virtualization/navigation.py +3 -1
- nautobot/virtualization/tables.py +10 -6
- nautobot/virtualization/templates/virtualization/cluster.html +13 -13
- nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
- nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_models.py +20 -5
- nautobot/virtualization/tests/test_views.py +3 -5
- nautobot/virtualization/urls.py +0 -11
- nautobot/virtualization/views.py +5 -122
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +209 -0
- nautobot/vpn/filters.py +233 -0
- nautobot/vpn/forms.py +486 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +527 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +380 -0
- nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +341 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +294 -0
- nautobot/vpn/tests/test_models.py +97 -0
- nautobot/vpn/tests/test_views.py +281 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +437 -0
- nautobot/wireless/filters.py +0 -8
- nautobot/wireless/navigation.py +3 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
- nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/core/management/commands/check_job_approval_status.py +0 -47
- nautobot/core/templates/search_form.html +0 -9
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/extras/templates/extras/job_approval_request.html +0 -134
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
- nautobot/ipam/mixins.py +0 -32
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
- nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
- nautobot/project-static/css/base.css +0 -1040
- nautobot/project-static/css/dark.css +0 -282
- nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
- nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
- nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
- nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
- nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
- nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
- nautobot/project-static/js/homepage_layout.js +0 -182
- nautobot/project-static/js/nav_menu.js +0 -250
- nautobot/project-static/js/theme.js +0 -133
- nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
- nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
- nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
- nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
- nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
- nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
- nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
- nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
- nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
- /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
|
@@ -5,9 +5,10 @@ import inspect
|
|
|
5
5
|
from logging import getLogger
|
|
6
6
|
|
|
7
7
|
from django.conf import settings
|
|
8
|
+
from django.conf.urls import include
|
|
8
9
|
from django.core.exceptions import ValidationError
|
|
9
10
|
from django.template.loader import get_template
|
|
10
|
-
from django.urls import get_resolver, URLPattern
|
|
11
|
+
from django.urls import clear_url_caches, get_resolver, path, URLPattern
|
|
11
12
|
from packaging import version
|
|
12
13
|
|
|
13
14
|
from nautobot.core.apps import (
|
|
@@ -17,10 +18,9 @@ from nautobot.core.apps import (
|
|
|
17
18
|
register_menu_items,
|
|
18
19
|
)
|
|
19
20
|
from nautobot.core.signals import nautobot_database_ready
|
|
20
|
-
from nautobot.core.utils.
|
|
21
|
+
from nautobot.core.utils.module_loading import import_string_optional
|
|
21
22
|
from nautobot.extras.choices import BannerClassChoices
|
|
22
23
|
from nautobot.extras.plugins.exceptions import PluginImproperlyConfigured
|
|
23
|
-
from nautobot.extras.plugins.utils import import_object
|
|
24
24
|
from nautobot.extras.registry import register_datasource_contents, registry
|
|
25
25
|
from nautobot.extras.secrets import register_secrets_provider
|
|
26
26
|
|
|
@@ -105,9 +105,24 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
105
105
|
"""Callback after plugin app is loaded."""
|
|
106
106
|
# We don't call super().ready here because we don't need or use the on-ready behavior of a core Nautobot app
|
|
107
107
|
|
|
108
|
+
from nautobot.extras.plugins.urls import BASE_URL_TO_APP_LABEL, plugin_api_patterns, plugin_patterns
|
|
109
|
+
|
|
108
110
|
# Introspect URL patterns and models to make available to the installed-plugins detail UI view.
|
|
109
|
-
urlpatterns =
|
|
110
|
-
api_urlpatterns =
|
|
111
|
+
urlpatterns = import_string_optional(f"{self.__module__}.urls.urlpatterns")
|
|
112
|
+
api_urlpatterns = import_string_optional(f"{self.__module__}.api.urls.urlpatterns")
|
|
113
|
+
|
|
114
|
+
base_url = self.base_url or self.label
|
|
115
|
+
|
|
116
|
+
if urlpatterns is not None:
|
|
117
|
+
plugin_patterns.append(path(f"{base_url}/", include((urlpatterns, self.label))))
|
|
118
|
+
|
|
119
|
+
if api_urlpatterns is not None:
|
|
120
|
+
plugin_api_patterns.append(path(f"{base_url}/", include((api_urlpatterns, f"{self.label}-api"))))
|
|
121
|
+
|
|
122
|
+
if any([urlpatterns, api_urlpatterns]):
|
|
123
|
+
clear_url_caches()
|
|
124
|
+
|
|
125
|
+
BASE_URL_TO_APP_LABEL[base_url] = self.label
|
|
111
126
|
|
|
112
127
|
self.features = {
|
|
113
128
|
"api_urlpatterns": sorted(
|
|
@@ -123,36 +138,31 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
# Register banner function (if defined)
|
|
126
|
-
banner_function
|
|
127
|
-
if banner_function is not None:
|
|
141
|
+
if banner_function := import_string_optional(f"{self.__module__}.{self.banner_function}"):
|
|
128
142
|
register_banner_function(banner_function)
|
|
129
143
|
self.features["banner"] = True
|
|
130
144
|
|
|
131
145
|
# Register model validators (if defined)
|
|
132
|
-
validators
|
|
133
|
-
if validators is not None:
|
|
146
|
+
if validators := import_string_optional(f"{self.__module__}.{self.custom_validators}"):
|
|
134
147
|
register_custom_validators(validators)
|
|
135
148
|
self.features["custom_validators"] = sorted(set(validator.model for validator in validators))
|
|
136
149
|
|
|
137
150
|
# Register datasource contents (if defined)
|
|
138
|
-
datasource_contents
|
|
139
|
-
if datasource_contents is not None:
|
|
151
|
+
if datasource_contents := import_string_optional(f"{self.__module__}.{self.datasource_contents}"):
|
|
140
152
|
register_datasource_contents(datasource_contents)
|
|
141
153
|
self.features["datasource_contents"] = datasource_contents
|
|
142
154
|
|
|
143
155
|
# Register GraphQL types (if defined)
|
|
144
|
-
graphql_types
|
|
145
|
-
if graphql_types is not None:
|
|
156
|
+
if graphql_types := import_string_optional(f"{self.__module__}.{self.graphql_types}"):
|
|
146
157
|
register_graphql_types(graphql_types)
|
|
147
158
|
|
|
148
159
|
# Import jobs (if present)
|
|
149
160
|
# Note that we do *not* auto-call `register_jobs()` - the App is responsible for doing so when imported.
|
|
150
|
-
jobs
|
|
151
|
-
if jobs is not None:
|
|
161
|
+
if jobs := import_string_optional(f"{self.__module__}.{self.jobs}"):
|
|
152
162
|
self.features["jobs"] = jobs
|
|
153
163
|
|
|
154
164
|
# Import metrics (if present)
|
|
155
|
-
metrics =
|
|
165
|
+
metrics = import_string_optional(f"{self.__module__}.{self.metrics}")
|
|
156
166
|
if metrics is not None and self.name not in settings.METRICS_DISABLED_APPS:
|
|
157
167
|
register_metrics(metrics)
|
|
158
168
|
self.features["metrics"] = [] # Initialize as empty, to be filled by the signal handler
|
|
@@ -161,19 +171,16 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
161
171
|
nautobot_database_ready.connect(signal_callback, sender=self)
|
|
162
172
|
|
|
163
173
|
# Register plugin navigation menu items (if defined)
|
|
164
|
-
menu_items
|
|
165
|
-
if menu_items is not None:
|
|
174
|
+
if menu_items := import_string_optional(f"{self.__module__}.{self.menu_items}"):
|
|
166
175
|
register_plugin_menu_items(self.verbose_name, menu_items)
|
|
167
176
|
self.features["nav_menu"] = menu_items
|
|
168
177
|
|
|
169
|
-
homepage_layout
|
|
170
|
-
if homepage_layout is not None:
|
|
178
|
+
if homepage_layout := import_string_optional(f"{self.__module__}.{self.homepage_layout}"):
|
|
171
179
|
register_homepage_panels(self.path, self.label, homepage_layout)
|
|
172
180
|
self.features["home_page"] = homepage_layout
|
|
173
181
|
|
|
174
182
|
# Register template content (if defined)
|
|
175
|
-
template_extensions
|
|
176
|
-
if template_extensions is not None:
|
|
183
|
+
if template_extensions := import_string_optional(f"{self.__module__}.{self.template_extensions}"):
|
|
177
184
|
register_template_extensions(template_extensions)
|
|
178
185
|
self.features["template_extensions"] = sorted(set(extension.model for extension in template_extensions))
|
|
179
186
|
|
|
@@ -185,15 +192,13 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
185
192
|
pass
|
|
186
193
|
|
|
187
194
|
# Register secrets providers (if any)
|
|
188
|
-
secrets_providers
|
|
189
|
-
if secrets_providers is not None:
|
|
195
|
+
if secrets_providers := import_string_optional(f"{self.__module__}.{self.secrets_providers}"):
|
|
190
196
|
for secrets_provider in secrets_providers:
|
|
191
197
|
register_secrets_provider(secrets_provider)
|
|
192
198
|
self.features["secrets_providers"] = secrets_providers
|
|
193
199
|
|
|
194
200
|
# Register custom filters (if any)
|
|
195
|
-
filter_extensions
|
|
196
|
-
if filter_extensions is not None:
|
|
201
|
+
if filter_extensions := import_string_optional(f"{self.__module__}.{self.filter_extensions}"):
|
|
197
202
|
register_filter_extensions(filter_extensions, self.name)
|
|
198
203
|
self.features["filter_extensions"] = {"filterset_fields": [], "filterform_fields": []}
|
|
199
204
|
for filter_extension in filter_extensions:
|
|
@@ -207,8 +212,7 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
207
212
|
)
|
|
208
213
|
|
|
209
214
|
# Register override view (if any)
|
|
210
|
-
override_views
|
|
211
|
-
if override_views is not None:
|
|
215
|
+
if override_views := import_string_optional(f"{self.__module__}.{self.override_views}"):
|
|
212
216
|
for qualified_view_name, view in override_views.items():
|
|
213
217
|
view_class_name = view.view_class.__name__ if hasattr(view, "view_class") else view.cls.__name__
|
|
214
218
|
self.features.setdefault("overridden_views", []).append(
|
|
@@ -272,17 +276,11 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
272
276
|
|
|
273
277
|
def _register_table_extensions(self):
|
|
274
278
|
"""Register tables extensions (if any)."""
|
|
275
|
-
table_extensions
|
|
276
|
-
if table_extensions is not None:
|
|
279
|
+
if table_extensions := import_string_optional(f"{self.__module__}.{self.table_extensions}"):
|
|
277
280
|
register_table_extensions(table_extensions, self.name)
|
|
278
281
|
self.features["table_extensions"] = get_table_extension_features(table_extensions)
|
|
279
282
|
|
|
280
283
|
|
|
281
|
-
@class_deprecated_in_favor_of(NautobotAppConfig)
|
|
282
|
-
class PluginConfig(NautobotAppConfig):
|
|
283
|
-
pass
|
|
284
|
-
|
|
285
|
-
|
|
286
284
|
#
|
|
287
285
|
# Template content injection
|
|
288
286
|
#
|
|
@@ -413,11 +411,6 @@ class TemplateExtension:
|
|
|
413
411
|
raise NotImplementedError
|
|
414
412
|
|
|
415
413
|
|
|
416
|
-
@class_deprecated_in_favor_of(TemplateExtension)
|
|
417
|
-
class PluginTemplateExtension(TemplateExtension):
|
|
418
|
-
pass
|
|
419
|
-
|
|
420
|
-
|
|
421
414
|
def register_template_extensions(class_list):
|
|
422
415
|
"""
|
|
423
416
|
Register a list of TemplateExtension classes
|
|
@@ -444,11 +437,6 @@ class Banner:
|
|
|
444
437
|
self.banner_class = banner_class
|
|
445
438
|
|
|
446
439
|
|
|
447
|
-
@class_deprecated_in_favor_of(Banner)
|
|
448
|
-
class PluginBanner(Banner):
|
|
449
|
-
pass
|
|
450
|
-
|
|
451
|
-
|
|
452
440
|
def register_banner_function(function):
|
|
453
441
|
"""
|
|
454
442
|
Register a function that may return a Banner object.
|
|
@@ -494,11 +482,6 @@ class FilterExtension:
|
|
|
494
482
|
filterform_fields = {}
|
|
495
483
|
|
|
496
484
|
|
|
497
|
-
@class_deprecated_in_favor_of(FilterExtension)
|
|
498
|
-
class PluginFilterExtension(FilterExtension):
|
|
499
|
-
pass
|
|
500
|
-
|
|
501
|
-
|
|
502
485
|
def register_filter_extensions(filter_extensions, plugin_name):
|
|
503
486
|
"""
|
|
504
487
|
Register a list of FilterExtension classes
|
|
@@ -789,11 +772,6 @@ class CustomValidator:
|
|
|
789
772
|
raise NotImplementedError
|
|
790
773
|
|
|
791
774
|
|
|
792
|
-
@class_deprecated_in_favor_of(CustomValidator)
|
|
793
|
-
class PluginCustomValidator(CustomValidator):
|
|
794
|
-
pass
|
|
795
|
-
|
|
796
|
-
|
|
797
775
|
def register_custom_validators(class_list):
|
|
798
776
|
"""
|
|
799
777
|
Register a list of CustomValidator classes
|
|
@@ -112,29 +112,7 @@
|
|
|
112
112
|
"availability": "Open Source"
|
|
113
113
|
"icon": >-
|
|
114
114
|
data:image/png;base64,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
|
|
115
|
-
|
|
116
|
-
"use_cases":
|
|
117
|
-
- "Data Management"
|
|
118
|
-
- "Data Hygiene"
|
|
119
|
-
- "Enterprise Governance"
|
|
120
|
-
"requires": []
|
|
121
|
-
"docs": "https://docs.nautobot.com/projects/data-validation/en/latest/"
|
|
122
|
-
"headline": >-
|
|
123
|
-
Guarantee Enterprise Data Governance with proper data hygiene by defining the right business rules for your
|
|
124
|
-
data.
|
|
125
|
-
"description": >-
|
|
126
|
-
Data Validation Engine offers a streamlined no-code user experience to create data validation rules using Regular
|
|
127
|
-
Expressions and min/mix rules ensuring corporate standards are enforced. It also supports advanced and complex
|
|
128
|
-
business rule logic by using the Data Validation APIs (with code).
|
|
129
|
-
"package_name": "nautobot_data_validation_engine"
|
|
130
|
-
"author": "Network to Code (NTC)"
|
|
131
|
-
"display":
|
|
132
|
-
"cloud": true
|
|
133
|
-
"oss": true
|
|
134
|
-
"enterprise": true
|
|
135
|
-
"availability": "Open Source"
|
|
136
|
-
"icon": >-
|
|
137
|
-
data:image/png;base64,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
|
|
115
|
+
# TODO: remove data_validation_engine from marketplace repo so it doesn't get reintroduced here by accident
|
|
138
116
|
- "name": "Design Builder"
|
|
139
117
|
"use_cases":
|
|
140
118
|
- "Network Design"
|
|
@@ -20,38 +20,40 @@ class InstalledAppsTable(tables.Table):
|
|
|
20
20
|
{% if record.home_url %}
|
|
21
21
|
<a href="{% url record.home_url %}" class="btn btn-primary btn-xs" title="Home">
|
|
22
22
|
{% else %}
|
|
23
|
-
<a
|
|
23
|
+
<a class="btn btn-primary btn-xs disabled" aria-disabled="true" title="No home link provided">
|
|
24
24
|
{% endif %}
|
|
25
25
|
<i class="mdi mdi-home"></i>
|
|
26
26
|
</a>
|
|
27
27
|
{% if record.config_url %}
|
|
28
28
|
<a href="{% url record.config_url %}" class="btn btn-warning btn-xs" title="Configure">
|
|
29
29
|
{% else %}
|
|
30
|
-
<a
|
|
30
|
+
<a class="btn btn-warning btn-xs disabled" aria-disabled="true" title="No configuration link provided">
|
|
31
31
|
{% endif %}
|
|
32
32
|
<i class="mdi mdi-cog"></i>
|
|
33
33
|
</a>
|
|
34
34
|
{% if record.docs_url %}
|
|
35
35
|
<a href="{% url record.docs_url %}" class="btn btn-info btn-xs" title="Docs">
|
|
36
36
|
{% else %}
|
|
37
|
-
<a
|
|
37
|
+
<a class="btn btn-info btn-xs disabled" aria-disabled="true" title="No docs provided">
|
|
38
38
|
{% endif %}
|
|
39
39
|
<i class="mdi mdi-book-open-page-variant"></i>
|
|
40
40
|
</a>
|
|
41
41
|
""",
|
|
42
|
-
attrs={"td": {"class": "text-
|
|
42
|
+
attrs={"td": {"class": "d-print-none text-end text-nowrap nb-w-0"}, "th": {"class": "nb-actionable nb-w-0"}},
|
|
43
43
|
verbose_name="",
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
class Meta:
|
|
47
47
|
attrs = {
|
|
48
|
-
"class": "table table-hover table-headings",
|
|
48
|
+
"class": "table table-hover nb-table-headings",
|
|
49
49
|
}
|
|
50
50
|
default_columns = ("name", "headline", "version", "actions")
|
|
51
51
|
|
|
52
|
-
def __init__(self, *args, user=None, **kwargs):
|
|
52
|
+
def __init__(self, *args, user=None, configurable=False, **kwargs):
|
|
53
53
|
super().__init__(*args, **kwargs)
|
|
54
54
|
|
|
55
|
+
self.configurable = configurable
|
|
56
|
+
|
|
55
57
|
if self.empty_text is None:
|
|
56
58
|
self.empty_text = "No installed Apps found"
|
|
57
59
|
|
nautobot/extras/plugins/urls.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
from django.apps import apps
|
|
2
|
-
from django.conf import settings
|
|
3
|
-
from django.conf.urls import include
|
|
4
1
|
from django.urls import path
|
|
5
2
|
|
|
6
|
-
from nautobot.extras.plugins.utils import import_object
|
|
7
|
-
|
|
8
3
|
from . import views
|
|
9
4
|
|
|
5
|
+
BASE_URL_TO_APP_LABEL = {}
|
|
6
|
+
|
|
10
7
|
# Initialize URL base, API, and admin URL patterns for plugins
|
|
11
8
|
apps_patterns = [
|
|
12
9
|
path("installed-apps/", views.InstalledAppsView.as_view(), name="apps_list"),
|
|
@@ -30,19 +27,3 @@ plugin_api_patterns = [
|
|
|
30
27
|
),
|
|
31
28
|
]
|
|
32
29
|
plugin_admin_patterns = []
|
|
33
|
-
|
|
34
|
-
# Register base/API URL patterns for each plugin
|
|
35
|
-
for plugin_path in settings.PLUGINS:
|
|
36
|
-
plugin_name = plugin_path.split(".")[-1]
|
|
37
|
-
app = apps.get_app_config(plugin_name)
|
|
38
|
-
base_url = app.base_url or app.label
|
|
39
|
-
|
|
40
|
-
# Check if the plugin specifies any base URLs
|
|
41
|
-
urlpatterns = import_object(f"{plugin_path}.urls.urlpatterns")
|
|
42
|
-
if urlpatterns is not None:
|
|
43
|
-
plugin_patterns.append(path(f"{base_url}/", include((urlpatterns, app.label))))
|
|
44
|
-
|
|
45
|
-
# Check if the plugin specifies any API URLs
|
|
46
|
-
urlpatterns = import_object(f"{plugin_path}.api.urls.urlpatterns")
|
|
47
|
-
if urlpatterns is not None:
|
|
48
|
-
plugin_api_patterns.append(path(f"{base_url}/", include((urlpatterns, f"{app.label}-api"))))
|
nautobot/extras/plugins/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ Plugin utilities.
|
|
|
4
4
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
import logging
|
|
7
|
-
import sys
|
|
8
7
|
|
|
9
8
|
from django.core.exceptions import ImproperlyConfigured
|
|
10
9
|
from django.utils.module_loading import import_string
|
|
@@ -17,37 +16,6 @@ from .exceptions import PluginImproperlyConfigured, PluginNotFound
|
|
|
17
16
|
logger = logging.getLogger(__name__)
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
def import_object(module_and_object):
|
|
21
|
-
"""
|
|
22
|
-
Import a specific object from a specific module by name, such as "nautobot.extras.plugins.utils.import_object".
|
|
23
|
-
|
|
24
|
-
Returns the imported object, or None if it doesn't exist.
|
|
25
|
-
"""
|
|
26
|
-
target_module_name, object_name = module_and_object.rsplit(".", 1)
|
|
27
|
-
module_hierarchy = target_module_name.split(".")
|
|
28
|
-
|
|
29
|
-
# Iterate through the module hierarchy, checking for the existence of each successive submodule.
|
|
30
|
-
# We have to do this rather than jumping directly to calling find_spec(target_module_name)
|
|
31
|
-
# because find_spec will raise a ModuleNotFoundError if any parent module of target_module_name does not exist.
|
|
32
|
-
module_name = ""
|
|
33
|
-
for module_component in module_hierarchy:
|
|
34
|
-
module_name = f"{module_name}.{module_component}" if module_name else module_component
|
|
35
|
-
spec = importlib.util.find_spec(module_name)
|
|
36
|
-
if spec is None:
|
|
37
|
-
# No such module
|
|
38
|
-
return None
|
|
39
|
-
|
|
40
|
-
# Okay, target_module_name exists. Load it if not already loaded
|
|
41
|
-
if target_module_name in sys.modules:
|
|
42
|
-
module = sys.modules[target_module_name]
|
|
43
|
-
else:
|
|
44
|
-
module = importlib.util.module_from_spec(spec)
|
|
45
|
-
sys.modules[target_module_name] = module
|
|
46
|
-
spec.loader.exec_module(module)
|
|
47
|
-
|
|
48
|
-
return getattr(module, object_name, None)
|
|
49
|
-
|
|
50
|
-
|
|
51
19
|
def load_plugins(settings):
|
|
52
20
|
"""Process plugins and log errors if they can't be loaded."""
|
|
53
21
|
for plugin_name in settings.PLUGINS:
|
|
@@ -77,7 +45,7 @@ def load_plugin(plugin_name, settings):
|
|
|
77
45
|
except AttributeError as err:
|
|
78
46
|
raise PluginImproperlyConfigured(
|
|
79
47
|
f"Plugin {plugin_name} does not provide a 'config' variable. This should be defined in the plugin's "
|
|
80
|
-
f"__init__.py file and point to the
|
|
48
|
+
f"__init__.py file and point to the NautobotAppConfig subclass."
|
|
81
49
|
) from err
|
|
82
50
|
|
|
83
51
|
# Validate user-provided configuration settings and assign defaults. Plugin
|
|
@@ -7,26 +7,26 @@ from nautobot.extras.registry import registry
|
|
|
7
7
|
from nautobot.extras.utils import FeatureQuery
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def custom_validator_clean(
|
|
10
|
+
def custom_validator_clean(model_class):
|
|
11
11
|
"""
|
|
12
12
|
Decorator that wraps a models existing clean method to also execute registered plugin custom validators
|
|
13
13
|
|
|
14
|
-
:
|
|
14
|
+
Args:
|
|
15
|
+
model_class: The model class whose clean function is to be wrapped
|
|
15
16
|
"""
|
|
16
17
|
|
|
18
|
+
model_clean_func = model_class.clean
|
|
19
|
+
model_name = model_class._meta.label_lower
|
|
20
|
+
|
|
17
21
|
@wraps(model_clean_func)
|
|
18
22
|
def wrapper(model_instance):
|
|
19
23
|
# Run original model clean method
|
|
20
24
|
model_clean_func(model_instance)
|
|
21
25
|
|
|
22
|
-
# Run registered
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Note this registry holds instances of PluginCustomValidator registered from plugins
|
|
26
|
+
# Run App registered custom validators
|
|
27
|
+
# Note this registry holds instances of CustomValidator registered from plugins
|
|
26
28
|
# which is different than the `custom_validators` model features registry
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for custom_validator in custom_validators:
|
|
29
|
+
for custom_validator in registry["plugin_custom_validators"][model_name]:
|
|
30
30
|
# If the class has not overridden the specified method, we can skip it (because we know it
|
|
31
31
|
# will raise NotImplementedError).
|
|
32
32
|
if getattr(custom_validator, "clean") == getattr(CustomValidator, "clean"):
|
|
@@ -43,4 +43,4 @@ def wrap_model_clean_methods():
|
|
|
43
43
|
for app_label, models in FeatureQuery("custom_validators").as_dict():
|
|
44
44
|
for model in models:
|
|
45
45
|
model_class = apps.get_model(app_label=app_label, model_name=model)
|
|
46
|
-
model_class.clean = custom_validator_clean(model_class
|
|
46
|
+
model_class.clean = custom_validator_clean(model_class)
|
nautobot/extras/plugins/views.py
CHANGED
|
@@ -86,7 +86,6 @@ class InstalledAppsView(GenericView):
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
88
|
table = InstalledAppsTable
|
|
89
|
-
breadcrumbs = Breadcrumbs(items={"*": [ViewNameBreadcrumbItem(view_name="apps:apps_list", label="Installed Apps")]})
|
|
90
89
|
view_titles = Titles(titles={"*": "Installed Apps"})
|
|
91
90
|
|
|
92
91
|
def get(self, request):
|
|
@@ -96,7 +95,7 @@ class InstalledAppsView(GenericView):
|
|
|
96
95
|
for app_config in app_configs:
|
|
97
96
|
if app_data := extract_app_data(app_config, marketplace_data):
|
|
98
97
|
data.append(app_data)
|
|
99
|
-
table = self.table(data, user=request.user)
|
|
98
|
+
table = self.table(data, user=request.user, configurable=True)
|
|
100
99
|
|
|
101
100
|
paginate = {
|
|
102
101
|
"paginator_class": EnhancedPaginator,
|
|
@@ -250,9 +249,6 @@ class MarketplaceView(GenericView):
|
|
|
250
249
|
View for listing all available Apps.
|
|
251
250
|
"""
|
|
252
251
|
|
|
253
|
-
breadcrumbs = Breadcrumbs(
|
|
254
|
-
items={"generic": [ViewNameBreadcrumbItem(view_name="apps:apps_marketplace", label="Apps Marketplace")]}
|
|
255
|
-
)
|
|
256
252
|
view_titles = Titles(titles={"generic": "Apps Marketplace"})
|
|
257
253
|
|
|
258
254
|
def get(self, request):
|
nautobot/extras/querysets.py
CHANGED
|
@@ -19,10 +19,6 @@ class ConfigContextQuerySet(RestrictedQuerySet):
|
|
|
19
19
|
# `device_type` for Device; `type` for VirtualMachine
|
|
20
20
|
device_type = getattr(obj, "device_type", None)
|
|
21
21
|
|
|
22
|
-
# Virtualization cluster for VirtualMachine
|
|
23
|
-
cluster = getattr(obj, "cluster", None)
|
|
24
|
-
cluster_group = getattr(cluster, "cluster_group", None)
|
|
25
|
-
|
|
26
22
|
device_redundancy_group = getattr(obj, "device_redundancy_group", None)
|
|
27
23
|
|
|
28
24
|
# Get the group of the assigned tenant, if any
|
|
@@ -45,8 +41,6 @@ class ConfigContextQuerySet(RestrictedQuerySet):
|
|
|
45
41
|
Q(roles=role) | Q(roles=None),
|
|
46
42
|
Q(device_types=device_type) | Q(device_types=None),
|
|
47
43
|
Q(platforms=obj.platform) | Q(platforms=None),
|
|
48
|
-
Q(cluster_groups=cluster_group) | Q(cluster_groups=None),
|
|
49
|
-
Q(clusters=cluster) | Q(clusters=None),
|
|
50
44
|
Q(device_redundancy_groups=device_redundancy_group) | Q(device_redundancy_groups=None),
|
|
51
45
|
Q(tenant_groups__in=tenant_groups) | Q(tenant_groups=None),
|
|
52
46
|
Q(tenants=tenant) | Q(tenants=None),
|
|
@@ -55,6 +49,16 @@ class ConfigContextQuerySet(RestrictedQuerySet):
|
|
|
55
49
|
if settings.CONFIG_CONTEXT_DYNAMIC_GROUPS_ENABLED:
|
|
56
50
|
query.append(Q(dynamic_groups__in=obj.dynamic_groups) | Q(dynamic_groups=None))
|
|
57
51
|
|
|
52
|
+
# `clusters` for Device, `cluster` for VirtualMachine
|
|
53
|
+
if obj._meta.model_name == "device":
|
|
54
|
+
query.append(Q(clusters__in=obj.clusters.all()) | Q(clusters=None))
|
|
55
|
+
query.append(
|
|
56
|
+
Q(cluster_groups__in=obj.clusters.values_list("cluster_group", flat=True)) | Q(cluster_groups=None)
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
query.append(Q(clusters=obj.cluster) | Q(clusters=None))
|
|
60
|
+
query.append(Q(cluster_groups=obj.cluster.cluster_group) | Q(cluster_groups=None))
|
|
61
|
+
|
|
58
62
|
queryset = (
|
|
59
63
|
self.filter(
|
|
60
64
|
*query,
|
|
@@ -119,8 +123,6 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
|
|
|
119
123
|
}
|
|
120
124
|
base_query = Q(
|
|
121
125
|
Q(platforms=OuterRef("platform")) | Q(platforms=None),
|
|
122
|
-
Q(cluster_groups=OuterRef("cluster__cluster_group")) | Q(cluster_groups=None),
|
|
123
|
-
Q(clusters=OuterRef("cluster")) | Q(clusters=None),
|
|
124
126
|
Q(tenants=OuterRef("tenant")) | Q(tenants=None),
|
|
125
127
|
Q(tags__pk__in=Subquery(TaggedItem.objects.filter(**tag_query_filters).values_list("tag_id", flat=True)))
|
|
126
128
|
| Q(tags=None),
|
|
@@ -138,8 +140,14 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
|
|
|
138
140
|
(Q(device_redundancy_groups=OuterRef("device_redundancy_group")) | Q(device_redundancy_groups=None)),
|
|
139
141
|
Q.AND,
|
|
140
142
|
)
|
|
143
|
+
# For devices, handle clusters as ManyToMany relationship
|
|
144
|
+
base_query.add((Q(clusters=OuterRef("clusters")) | Q(clusters=None)), Q.AND)
|
|
145
|
+
base_query.add((Q(cluster_groups=OuterRef("clusters__cluster_group")) | Q(cluster_groups=None)), Q.AND)
|
|
141
146
|
else:
|
|
142
147
|
location_query_string = "cluster__location"
|
|
148
|
+
# For virtual machines, handle cluster as ForeignKey relationship
|
|
149
|
+
base_query.add((Q(clusters=OuterRef("cluster")) | Q(clusters=None)), Q.AND)
|
|
150
|
+
base_query.add((Q(cluster_groups=OuterRef("cluster__cluster_group")) | Q(cluster_groups=None)), Q.AND)
|
|
143
151
|
|
|
144
152
|
location_query = Q(locations=None) | Q(locations=OuterRef(location_query_string))
|
|
145
153
|
for _ in range(Location.objects.max_depth + 1):
|
|
@@ -254,17 +262,5 @@ class ScheduledJobExtendedQuerySet(RestrictedQuerySet):
|
|
|
254
262
|
Return only ScheduledJob instances that are enabled and approved (if approval required)
|
|
255
263
|
"""
|
|
256
264
|
return self.filter(
|
|
257
|
-
Q(enabled=True) & Q(Q(approval_required=True,
|
|
265
|
+
Q(enabled=True) & Q(Q(approval_required=True, decision_date__isnull=False) | Q(approval_required=False))
|
|
258
266
|
)
|
|
259
|
-
|
|
260
|
-
def approved(self):
|
|
261
|
-
"""
|
|
262
|
-
Return only ScheduledJob instances that require approval and are approved
|
|
263
|
-
"""
|
|
264
|
-
return self.filter(approval_required=True, approved_at__isnull=False)
|
|
265
|
-
|
|
266
|
-
def needs_approved(self):
|
|
267
|
-
"""
|
|
268
|
-
Return only ScheduledJob instances that require approval and are not approved
|
|
269
|
-
"""
|
|
270
|
-
return self.filter(approval_required=True, approved_at__isnull=True).order_by("start_time")
|
nautobot/extras/signals.py
CHANGED
|
@@ -22,6 +22,7 @@ import redis.exceptions
|
|
|
22
22
|
|
|
23
23
|
from nautobot.core.celery import app, import_jobs
|
|
24
24
|
from nautobot.core.models import BaseModel
|
|
25
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
25
26
|
from nautobot.core.utils.logging import sanitize
|
|
26
27
|
from nautobot.extras.choices import ButtonClassChoices, JobResultStatusChoices, ObjectChangeActionChoices
|
|
27
28
|
from nautobot.extras.constants import CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL
|
|
@@ -142,9 +143,11 @@ def invalidate_models_cache(sender, **kwargs):
|
|
|
142
143
|
|
|
143
144
|
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
144
145
|
# TODO: *maybe* target more narrowly, e.g. only clear the cache for specific related content-types?
|
|
145
|
-
|
|
146
|
+
cache_key = construct_cache_key(manager, method_name="get_for_model", branch_aware=True)
|
|
147
|
+
cache.delete_pattern(f"{cache_key}(*)")
|
|
146
148
|
if hasattr(manager, "keys_for_model"):
|
|
147
|
-
|
|
149
|
+
cache_key = construct_cache_key(manager, method_name="keys_for_model", branch_aware=True)
|
|
150
|
+
cache.delete_pattern(f"{cache_key}(*)")
|
|
148
151
|
|
|
149
152
|
|
|
150
153
|
@receiver(post_delete, sender=CustomField)
|
|
@@ -155,9 +158,10 @@ def invalidate_choices_cache(sender, instance, **kwargs):
|
|
|
155
158
|
"""Invalidate the choices cache for CustomFields."""
|
|
156
159
|
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
157
160
|
if sender is CustomField:
|
|
158
|
-
|
|
161
|
+
cache_key = construct_cache_key(instance, method_name="choices", branch_aware=True)
|
|
159
162
|
else:
|
|
160
|
-
|
|
163
|
+
cache_key = construct_cache_key(instance.custom_field, method_name="choices", branch_aware=True)
|
|
164
|
+
cache.delete(cache_key)
|
|
161
165
|
|
|
162
166
|
|
|
163
167
|
@receiver(post_save, sender=Relationship)
|
|
@@ -165,13 +169,14 @@ def invalidate_choices_cache(sender, instance, **kwargs):
|
|
|
165
169
|
@receiver(post_delete, sender=Relationship)
|
|
166
170
|
def invalidate_relationship_models_cache(sender, **kwargs):
|
|
167
171
|
"""Invalidate the related-models caches for Relationships."""
|
|
168
|
-
for
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
for method_name in (
|
|
173
|
+
"get_for_model_source",
|
|
174
|
+
"get_for_model_destination",
|
|
171
175
|
):
|
|
172
176
|
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
173
177
|
# TODO: *maybe* target more narrowly, e.g. only clear the cache for specific related content-types?
|
|
174
|
-
|
|
178
|
+
cache_key = construct_cache_key(Relationship.objects, method_name=method_name, branch_aware=True)
|
|
179
|
+
cache.delete_pattern(f"{cache_key}(*)")
|
|
175
180
|
|
|
176
181
|
|
|
177
182
|
@receiver(post_save, sender=CustomField)
|
|
@@ -200,6 +205,16 @@ def _handle_changed_object_pre_save(sender, instance, raw=False, **kwargs):
|
|
|
200
205
|
_cache_obj_data_in_change_context(ObjectChangeActionChoices.ACTION_UPDATE, sender, instance)
|
|
201
206
|
|
|
202
207
|
|
|
208
|
+
@receiver(post_save, sender=GitRepository)
|
|
209
|
+
@receiver(post_delete, sender=GitRepository)
|
|
210
|
+
def invalidate_gitrepository_provided_contents_cache(sender, **kwargs):
|
|
211
|
+
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
212
|
+
cache_key = construct_cache_key(
|
|
213
|
+
GitRepository.objects, method_name="get_for_provided_contents", branch_aware=True
|
|
214
|
+
)
|
|
215
|
+
cache.delete_pattern(f"{cache_key}(*)")
|
|
216
|
+
|
|
217
|
+
|
|
203
218
|
@receiver(post_save)
|
|
204
219
|
@receiver(m2m_changed)
|
|
205
220
|
def _handle_changed_object(sender, instance, raw=False, **kwargs):
|