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
nautobot/core/graphql/utils.py
CHANGED
|
@@ -9,7 +9,6 @@ from nautobot.core.filters import (
|
|
|
9
9
|
MultiValueFloatFilter,
|
|
10
10
|
MultiValueNumberFilter,
|
|
11
11
|
)
|
|
12
|
-
from nautobot.core.graphql import BigInteger
|
|
13
12
|
from nautobot.core.models.fields import slugify_dashes_to_underscores
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger(__name__)
|
|
@@ -41,7 +40,7 @@ def get_filtering_args_from_filterset(filterset_class):
|
|
|
41
40
|
(dict[graphene.Argument]): Filter Arguments organized in a dictionary
|
|
42
41
|
"""
|
|
43
42
|
|
|
44
|
-
args = {}
|
|
43
|
+
args = {"args": {}}
|
|
45
44
|
instance = filterset_class()
|
|
46
45
|
|
|
47
46
|
for filter_name, filter_field in instance.filters.items():
|
|
@@ -60,7 +59,7 @@ def get_filtering_args_from_filterset(filterset_class):
|
|
|
60
59
|
filter_field_class = type(filter_field)
|
|
61
60
|
|
|
62
61
|
if issubclass(filter_field_class, MultiValueBigNumberFilter):
|
|
63
|
-
field_type = graphene.List(
|
|
62
|
+
field_type = graphene.List(graphene.types.scalars.BigInt)
|
|
64
63
|
elif issubclass(filter_field_class, (MultiValueFloatFilter, MultiValueDecimalFilter)):
|
|
65
64
|
field_type = graphene.List(graphene.Float)
|
|
66
65
|
elif issubclass(filter_field_class, MultiValueNumberFilter):
|
|
@@ -82,13 +81,12 @@ def get_filtering_args_from_filterset(filterset_class):
|
|
|
82
81
|
required=False,
|
|
83
82
|
)
|
|
84
83
|
|
|
85
|
-
# Hack to
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# will still need to do an API migration to deprecate it. This argument was
|
|
89
|
-
# validated to be safe to keep even in Graphene 3.
|
|
84
|
+
# Hack to avoid conflict with `graphene.types.field.Field.description`.
|
|
85
|
+
if "description" in args:
|
|
86
|
+
args["args"].update({"description": args.pop("description")})
|
|
90
87
|
if "type" in args:
|
|
91
|
-
|
|
88
|
+
# for backwards compatibility with our filters in graphene v2 where `type` was a reserved keyword
|
|
89
|
+
args["args"].update({"_type": args["type"]})
|
|
92
90
|
|
|
93
91
|
return args
|
|
94
92
|
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -2,13 +2,16 @@ import codecs
|
|
|
2
2
|
import contextlib
|
|
3
3
|
from io import BytesIO
|
|
4
4
|
|
|
5
|
+
from django.apps import apps as global_apps
|
|
5
6
|
from django.conf import settings
|
|
6
7
|
from django.contrib.contenttypes.models import ContentType
|
|
7
8
|
from django.core.exceptions import (
|
|
8
9
|
PermissionDenied,
|
|
9
10
|
)
|
|
10
11
|
from django.db import transaction
|
|
12
|
+
from django.db.models import Q
|
|
11
13
|
from django.http import QueryDict
|
|
14
|
+
from django.urls import reverse
|
|
12
15
|
from rest_framework import exceptions as drf_exceptions
|
|
13
16
|
|
|
14
17
|
from nautobot.core.api.exceptions import SerializerNotFound
|
|
@@ -22,6 +25,12 @@ from nautobot.core.jobs.cleanup import LogsCleanup
|
|
|
22
25
|
from nautobot.core.jobs.groups import RefreshDynamicGroupCacheJobButtonReceiver, RefreshDynamicGroupCaches
|
|
23
26
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
24
27
|
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
28
|
+
from nautobot.data_validation import models
|
|
29
|
+
from nautobot.data_validation.custom_validators import (
|
|
30
|
+
BaseValidator,
|
|
31
|
+
get_data_compliance_classes_from_git_repo,
|
|
32
|
+
get_data_compliance_rules_map,
|
|
33
|
+
)
|
|
25
34
|
from nautobot.extras.datasources import (
|
|
26
35
|
ensure_git_repository,
|
|
27
36
|
git_repository_dry_run,
|
|
@@ -33,12 +42,15 @@ from nautobot.extras.jobs import (
|
|
|
33
42
|
ChoiceVar,
|
|
34
43
|
FileVar,
|
|
35
44
|
Job,
|
|
45
|
+
MultiChoiceVar,
|
|
36
46
|
ObjectVar,
|
|
37
47
|
RunJobTaskFailed,
|
|
38
48
|
StringVar,
|
|
39
49
|
TextVar,
|
|
40
50
|
)
|
|
41
51
|
from nautobot.extras.models import ExportTemplate, GitRepository, SavedView
|
|
52
|
+
from nautobot.extras.plugins import CustomValidator, ValidationError
|
|
53
|
+
from nautobot.extras.registry import registry
|
|
42
54
|
|
|
43
55
|
name = "System Jobs"
|
|
44
56
|
|
|
@@ -372,6 +384,151 @@ class ImportObjects(Job):
|
|
|
372
384
|
raise RunJobTaskFailed("CSV import not fully successful, see logs")
|
|
373
385
|
|
|
374
386
|
|
|
387
|
+
def get_data_compliance_rules():
|
|
388
|
+
"""Generate a list of Audit Ruleset classes that exist from the registry as well as from any Git Repositories."""
|
|
389
|
+
validators = []
|
|
390
|
+
for rule_sets in get_data_compliance_rules_map().values():
|
|
391
|
+
validators.extend(rule_sets)
|
|
392
|
+
|
|
393
|
+
# Get rules from Git Repositories
|
|
394
|
+
for repo in GitRepository.objects.get_for_provided_contents("data_validation.data_compliance_rule"):
|
|
395
|
+
validators.extend(get_data_compliance_classes_from_git_repo(repo))
|
|
396
|
+
return validators
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def get_data_compliance_choices():
|
|
400
|
+
"""Get data compliance choices from registry as well as from any Git Repositories."""
|
|
401
|
+
choices = []
|
|
402
|
+
for ruleset_class in get_data_compliance_rules():
|
|
403
|
+
choices.append((ruleset_class.__name__, ruleset_class.__name__))
|
|
404
|
+
|
|
405
|
+
choices.sort()
|
|
406
|
+
return choices
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def clean_compliance_rules_results_for_instance(instance, excluded_pks):
|
|
410
|
+
"""
|
|
411
|
+
Delete data compliance results generated from runs of RunRegisteredDataComplianceRules job,
|
|
412
|
+
which validates object against user-created rules.
|
|
413
|
+
e.g. UniqueValidationRules, RegularExpressionValidationRules, MinMaxValidationRules, and RequiredValidationRules.
|
|
414
|
+
|
|
415
|
+
The usage is that:
|
|
416
|
+
If the instance is valid against all user-created rules, then the previous data compliance results of the instance are deleted.
|
|
417
|
+
If the instance is invalid against any user-created rules, then this method deletes the existing data compliance results of the instance,
|
|
418
|
+
and preserves only the data compliance result from the most recent job run by including the pk of the result in the `excluded_pks` list.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
instance: The validated object to clean compliance results for.
|
|
422
|
+
excluded_pks: List of primary keys of compliance results to exclude from deletion.
|
|
423
|
+
"""
|
|
424
|
+
model_class = instance.__class__
|
|
425
|
+
model_custom_validators = registry["plugin_custom_validators"][model_class._meta.label_lower]
|
|
426
|
+
# Prep for compliance names to be deleted.
|
|
427
|
+
compliance_class_names_to_be_deleted = []
|
|
428
|
+
for cv in model_custom_validators:
|
|
429
|
+
if issubclass(cv, BaseValidator):
|
|
430
|
+
compliance_class_names_to_be_deleted.append(cv.__name__)
|
|
431
|
+
|
|
432
|
+
excluded_pks = excluded_pks or []
|
|
433
|
+
models.DataCompliance.objects.filter(
|
|
434
|
+
object_id=instance.id,
|
|
435
|
+
content_type=ContentType.objects.get_for_model(instance),
|
|
436
|
+
compliance_class_name__in=compliance_class_names_to_be_deleted,
|
|
437
|
+
).exclude(pk__in=excluded_pks).delete()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class RunRegisteredDataComplianceRules(Job):
|
|
441
|
+
"""Run the validate function on all registered DataComplianceRule classes and, optionally, the built-in data validation rules."""
|
|
442
|
+
|
|
443
|
+
class Meta:
|
|
444
|
+
name = "Run Registered Data Compliance Rules"
|
|
445
|
+
description = "Runs selected Data Compliance rule classes."
|
|
446
|
+
has_sensitive_variables = False
|
|
447
|
+
|
|
448
|
+
selected_data_compliance_rules = MultiChoiceVar(
|
|
449
|
+
choices=get_data_compliance_choices,
|
|
450
|
+
label="Select Data Compliance Rules",
|
|
451
|
+
required=False,
|
|
452
|
+
description="Not selecting any rules will run all rules listed.",
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
run_user_created_rules_in_report = BooleanVar(
|
|
456
|
+
label="Run user created validation rules?", description="Include user created data validation rules in report."
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
def run(self, *args, **kwargs):
|
|
460
|
+
"""Run the validate function on all given DataComplianceRule classes."""
|
|
461
|
+
selected_data_compliance_rules = kwargs.get("selected_data_compliance_rules", None)
|
|
462
|
+
|
|
463
|
+
compliance_classes = get_data_compliance_rules()
|
|
464
|
+
|
|
465
|
+
for compliance_class in sorted(
|
|
466
|
+
compliance_classes, key=lambda x: x.model.split(".")
|
|
467
|
+
): # sort by model.app_label and model.model_name
|
|
468
|
+
if selected_data_compliance_rules and compliance_class.__name__ not in selected_data_compliance_rules:
|
|
469
|
+
continue
|
|
470
|
+
self.logger.info(f"Running {compliance_class.__name__}")
|
|
471
|
+
app_label, model = compliance_class.model.split(".")
|
|
472
|
+
for obj in global_apps.get_model(app_label, model).objects.iterator():
|
|
473
|
+
ins = compliance_class(obj)
|
|
474
|
+
ins.enforce = False
|
|
475
|
+
ins.clean()
|
|
476
|
+
|
|
477
|
+
run_user_created_rules_in_report = kwargs.get("run_user_created_rules_in_report", False)
|
|
478
|
+
if run_user_created_rules_in_report:
|
|
479
|
+
self.logger.info("Running user created data validation rules")
|
|
480
|
+
self.report_for_validation_rules()
|
|
481
|
+
|
|
482
|
+
result_url = reverse("data_validation:datacompliance_list")
|
|
483
|
+
self.logger.info(f"View Data Compliance results [here]({result_url})")
|
|
484
|
+
|
|
485
|
+
@staticmethod
|
|
486
|
+
def report_for_validation_rules():
|
|
487
|
+
"""Run built-in data validation rules and add to report."""
|
|
488
|
+
query = (
|
|
489
|
+
Q(uniquevalidationrule__isnull=False) # pylint: disable=unsupported-binary-operation
|
|
490
|
+
| Q(regularexpressionvalidationrule__isnull=False)
|
|
491
|
+
| Q(minmaxvalidationrule__isnull=False)
|
|
492
|
+
| Q(requiredvalidationrule__isnull=False)
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Gather model classes that have any of the user created rules:
|
|
496
|
+
# UniqueValidationRules, RegularExpressionValidationRules, MinMaxValidationRules, and RequiredValidationRules.
|
|
497
|
+
model_classes = [ct.model_class() for ct in ContentType.objects.filter(query).distinct()]
|
|
498
|
+
|
|
499
|
+
# Gather custom validators of user created rules
|
|
500
|
+
validator_dicts = []
|
|
501
|
+
for model_class in model_classes:
|
|
502
|
+
model_custom_validators = registry["plugin_custom_validators"][model_class._meta.label_lower]
|
|
503
|
+
# Get only subclasses of BaseValidator
|
|
504
|
+
# BaseValidator is the validator that enforces the user created rules:
|
|
505
|
+
# UniqueValidationRules, RegularExpressionValidationRules, MinMaxValidationRules, and RequiredValidationRules.
|
|
506
|
+
# otherwise, we would get all validators (more than those dynamically created)
|
|
507
|
+
validator_dicts.extend(
|
|
508
|
+
[{cv: model_class} for cv in model_custom_validators if issubclass(cv, BaseValidator)]
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# Run validation on existing objects and add to report
|
|
512
|
+
for validator_dict in validator_dicts:
|
|
513
|
+
for validator, class_name in validator_dict.items():
|
|
514
|
+
if validator.clean == CustomValidator.clean:
|
|
515
|
+
continue
|
|
516
|
+
|
|
517
|
+
for validated_object in class_name.objects.iterator():
|
|
518
|
+
try:
|
|
519
|
+
validator(validated_object).clean(exclude_disabled_rules=False)
|
|
520
|
+
clean_compliance_rules_results_for_instance(instance=validated_object, excluded_pks=[])
|
|
521
|
+
except ValidationError as error:
|
|
522
|
+
result = validator.get_compliance_result(
|
|
523
|
+
validator,
|
|
524
|
+
instance=validated_object,
|
|
525
|
+
message=error.messages[0],
|
|
526
|
+
attribute=next(iter(error.message_dict.keys())),
|
|
527
|
+
valid=False,
|
|
528
|
+
)
|
|
529
|
+
clean_compliance_rules_results_for_instance(instance=validated_object, excluded_pks=[result.pk])
|
|
530
|
+
|
|
531
|
+
|
|
375
532
|
jobs = [
|
|
376
533
|
BulkDeleteObjects,
|
|
377
534
|
BulkEditObjects,
|
|
@@ -382,5 +539,6 @@ jobs = [
|
|
|
382
539
|
LogsCleanup,
|
|
383
540
|
RefreshDynamicGroupCaches,
|
|
384
541
|
RefreshDynamicGroupCacheJobButtonReceiver,
|
|
542
|
+
RunRegisteredDataComplianceRules,
|
|
385
543
|
]
|
|
386
544
|
register_jobs(*jobs)
|
|
@@ -133,6 +133,14 @@ class Command(BaseCommand):
|
|
|
133
133
|
)
|
|
134
134
|
from nautobot.tenancy.factory import TenantFactory, TenantGroupFactory
|
|
135
135
|
from nautobot.users.factory import UserFactory
|
|
136
|
+
from nautobot.vpn.factory import (
|
|
137
|
+
VPNFactory,
|
|
138
|
+
VPNPhase1PolicyFactory,
|
|
139
|
+
VPNPhase2PolicyFactory,
|
|
140
|
+
VPNProfileFactory,
|
|
141
|
+
VPNTunnelEndpointFactory,
|
|
142
|
+
VPNTunnelFactory,
|
|
143
|
+
)
|
|
136
144
|
from nautobot.wireless.factory import (
|
|
137
145
|
ControllerManagedDeviceGroupWithMembersFactory,
|
|
138
146
|
RadioProfileFactory,
|
|
@@ -171,14 +179,12 @@ class Command(BaseCommand):
|
|
|
171
179
|
)
|
|
172
180
|
# ...and some tags that apply to a random subset of content-types
|
|
173
181
|
_create_batch(TagFactory, 15, description="on some content-types")
|
|
174
|
-
_create_batch(UserFactory,
|
|
182
|
+
_create_batch(UserFactory, 10)
|
|
175
183
|
_create_batch(SavedViewFactory, 20)
|
|
176
184
|
_create_batch(ContactFactory, 20)
|
|
177
185
|
_create_batch(TeamFactory, 20)
|
|
178
|
-
_create_batch(TenantGroupFactory,
|
|
179
|
-
_create_batch(
|
|
180
|
-
_create_batch(TenantFactory, 10, description="without a parent group", has_tenant_group=False)
|
|
181
|
-
_create_batch(TenantFactory, 10, description="with a parent group", has_tenant_group=True)
|
|
186
|
+
_create_batch(TenantGroupFactory, 30)
|
|
187
|
+
_create_batch(TenantFactory, 30)
|
|
182
188
|
_create_batch(LocationTypeFactory, 7) # only 7 unique LocationTypes are hard-coded presently
|
|
183
189
|
# First 7 locations must be created in specific order so subsequent objects have valid parents to reference
|
|
184
190
|
_create_batch(LocationFactory, 7, description="as structure", has_parent=True)
|
|
@@ -192,7 +198,7 @@ class Command(BaseCommand):
|
|
|
192
198
|
_create_batch(VRFFactory, 20)
|
|
193
199
|
_create_batch(VLANGroupFactory, 20)
|
|
194
200
|
_create_batch(VLANFactory, 20)
|
|
195
|
-
for i in range(
|
|
201
|
+
for i in range(50):
|
|
196
202
|
_create_batch(
|
|
197
203
|
PrefixFactory,
|
|
198
204
|
1,
|
|
@@ -207,7 +213,15 @@ class Command(BaseCommand):
|
|
|
207
213
|
prefix=f"2001:db8:0:{i}::/64",
|
|
208
214
|
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
209
215
|
)
|
|
210
|
-
_create_batch(
|
|
216
|
+
_create_batch(
|
|
217
|
+
NamespaceFactory, 5, description="with a Tenant and without any Prefixes or IPAddresses", has_tenant=True
|
|
218
|
+
)
|
|
219
|
+
_create_batch(
|
|
220
|
+
NamespaceFactory,
|
|
221
|
+
5,
|
|
222
|
+
description="without a Tenant and without any Prefixes or IPAddresses",
|
|
223
|
+
has_tenant=False,
|
|
224
|
+
)
|
|
211
225
|
_create_batch(DeviceFamilyFactory, 20)
|
|
212
226
|
_create_batch(ManufacturerFactory, 8) # First 8 hard-coded Manufacturers
|
|
213
227
|
_create_batch(PlatformFactory, 20, description="with Manufacturers", has_manufacturer=True)
|
|
@@ -222,7 +236,7 @@ class Command(BaseCommand):
|
|
|
222
236
|
_create_batch(ConsoleServerPortTemplateFactory, 30)
|
|
223
237
|
_create_batch(RearPortTemplateFactory, 30)
|
|
224
238
|
_create_batch(FrontPortTemplateFactory, 30)
|
|
225
|
-
_create_batch(InterfaceTemplateFactory,
|
|
239
|
+
_create_batch(InterfaceTemplateFactory, 50)
|
|
226
240
|
_create_batch(PowerPortTemplateFactory, 30)
|
|
227
241
|
_create_batch(PowerOutletTemplateFactory, 30)
|
|
228
242
|
_create_batch(ModuleBayTemplateFactory, 60, description="without module families", has_module_family=False)
|
|
@@ -324,11 +338,16 @@ class Command(BaseCommand):
|
|
|
324
338
|
_create_batch(WirelessNetworksWithMembersFactory, 5, description="with members")
|
|
325
339
|
# make sure we have some supported data rates that have null relationships to make filter tests happy
|
|
326
340
|
_create_batch(SupportedDataRateFactory, 10, description="without any associated objects")
|
|
341
|
+
_create_batch(VPNPhase1PolicyFactory, 10)
|
|
342
|
+
_create_batch(VPNPhase2PolicyFactory, 10)
|
|
343
|
+
_create_batch(VPNProfileFactory, 20)
|
|
344
|
+
_create_batch(VPNFactory, 10)
|
|
345
|
+
_create_batch(VPNTunnelEndpointFactory, 20)
|
|
346
|
+
_create_batch(VPNTunnelFactory, 10)
|
|
327
347
|
_create_batch(JobQueueFactory, 10)
|
|
328
348
|
# make sure we have some tenants that have null relationships to make filter tests happy
|
|
329
349
|
_create_batch(TenantFactory, 10, description="without any associated objects")
|
|
330
350
|
# TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
|
|
331
|
-
# _create_batch(UserFactory, 10)
|
|
332
351
|
# _create_batch(RackFactory, 10)
|
|
333
352
|
# _create_batch(RackReservationFactory, 10)
|
|
334
353
|
# _create_batch(ClusterTypeFactory, 10)
|
nautobot/core/models/__init__.py
CHANGED
|
@@ -11,15 +11,23 @@ from django.utils.encoding import is_protected_type
|
|
|
11
11
|
from django.utils.functional import classproperty
|
|
12
12
|
|
|
13
13
|
from nautobot.core.models.managers import BaseManager
|
|
14
|
-
from nautobot.core.models.querysets import
|
|
14
|
+
from nautobot.core.models.querysets import (
|
|
15
|
+
ClusterToClustersQuerySetMixin,
|
|
16
|
+
CompositeKeyQuerySetMixin,
|
|
17
|
+
LocationToLocationsQuerySetMixin,
|
|
18
|
+
RestrictedQuerySet,
|
|
19
|
+
)
|
|
15
20
|
from nautobot.core.models.utils import construct_composite_key, construct_natural_slug, deconstruct_composite_key
|
|
21
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
16
22
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
17
23
|
|
|
18
24
|
__all__ = (
|
|
19
25
|
"BaseManager",
|
|
20
26
|
"BaseModel",
|
|
27
|
+
"ClusterToClustersQuerySetMixin",
|
|
21
28
|
"CompositeKeyQuerySetMixin",
|
|
22
29
|
"ContentTypeRelatedQuerySet",
|
|
30
|
+
"LocationToLocationsQuerySetMixin",
|
|
23
31
|
"RestrictedQuerySet",
|
|
24
32
|
"construct_composite_key",
|
|
25
33
|
"construct_natural_slug",
|
|
@@ -53,6 +61,7 @@ class BaseModel(models.Model):
|
|
|
53
61
|
is_metadata_associable_model = True
|
|
54
62
|
is_saved_view_model = False # SavedViewMixin overrides this to default True
|
|
55
63
|
is_cloud_resource_type_model = False # CloudResourceTypeMixin overrides this to default True
|
|
64
|
+
is_approval_workflow_model = False # ApprovableModelMixin overrides this to default True
|
|
56
65
|
|
|
57
66
|
associated_object_metadata = GenericRelation(
|
|
58
67
|
"extras.ObjectMetadata",
|
|
@@ -60,6 +69,12 @@ class BaseModel(models.Model):
|
|
|
60
69
|
object_id_field="assigned_object_id",
|
|
61
70
|
related_query_name="associated_object_metadata_%(app_label)s_%(class)s", # e.g. 'associated_object_metadata_dcim_device'
|
|
62
71
|
)
|
|
72
|
+
associated_data_compliance = GenericRelation(
|
|
73
|
+
"data_validation.DataCompliance",
|
|
74
|
+
content_type_field="content_type",
|
|
75
|
+
object_id_field="object_id",
|
|
76
|
+
related_query_name="associated_data_compliance_%(app_label)s_%(class)s", # e.g. 'associated_data_compliance_dcim_device'
|
|
77
|
+
)
|
|
63
78
|
|
|
64
79
|
class Meta:
|
|
65
80
|
abstract = True
|
|
@@ -108,7 +123,7 @@ class BaseModel(models.Model):
|
|
|
108
123
|
|
|
109
124
|
Necessary for use with _content_type_cached and management commands.
|
|
110
125
|
"""
|
|
111
|
-
return
|
|
126
|
+
return construct_cache_key(cls, method_name="_content_type")
|
|
112
127
|
|
|
113
128
|
@classproperty # https://github.com/PyCQA/pylint-django/issues/240
|
|
114
129
|
def _content_type_cached(cls): # pylint: disable=no-self-argument
|
nautobot/core/models/fields.py
CHANGED
|
@@ -244,8 +244,9 @@ class NaturalOrderingField(models.CharField):
|
|
|
244
244
|
"""
|
|
245
245
|
A field which stores a naturalized representation of its target field, to be used for ordering its parent model.
|
|
246
246
|
|
|
247
|
-
:
|
|
248
|
-
|
|
247
|
+
Args:
|
|
248
|
+
target_field (str): Name of the field of the parent model to be naturalized
|
|
249
|
+
naturalize_function (function): The function used to generate a naturalized value (optional)
|
|
249
250
|
"""
|
|
250
251
|
|
|
251
252
|
description = "Stores a representation of its target field suitable for natural ordering"
|
nautobot/core/models/generics.py
CHANGED
|
@@ -4,13 +4,20 @@ from nautobot.core.models import BaseModel
|
|
|
4
4
|
from nautobot.core.models.fields import TagsField
|
|
5
5
|
from nautobot.extras.models.change_logging import ChangeLoggedModel
|
|
6
6
|
from nautobot.extras.models.customfields import CustomFieldModel
|
|
7
|
-
from nautobot.extras.models.mixins import
|
|
7
|
+
from nautobot.extras.models.mixins import (
|
|
8
|
+
ContactMixin,
|
|
9
|
+
DataComplianceModelMixin,
|
|
10
|
+
DynamicGroupsModelMixin,
|
|
11
|
+
NotesMixin,
|
|
12
|
+
SavedViewMixin,
|
|
13
|
+
)
|
|
8
14
|
from nautobot.extras.models.relationships import RelationshipModel
|
|
9
15
|
|
|
10
16
|
logger = logging.getLogger(__name__)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class OrganizationalModel(
|
|
20
|
+
DataComplianceModelMixin,
|
|
14
21
|
ChangeLoggedModel,
|
|
15
22
|
ContactMixin,
|
|
16
23
|
CustomFieldModel,
|
|
@@ -35,6 +42,7 @@ class OrganizationalModel(
|
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
class PrimaryModel(
|
|
45
|
+
DataComplianceModelMixin,
|
|
38
46
|
ChangeLoggedModel,
|
|
39
47
|
ContactMixin,
|
|
40
48
|
CustomFieldModel,
|
|
@@ -60,6 +60,6 @@ class NameColorContentTypesModel(
|
|
|
60
60
|
def get_color_display(self):
|
|
61
61
|
if self.color:
|
|
62
62
|
return format_html(
|
|
63
|
-
'<span class="label color-block" style="background-color: #{}"> </span>', self.color
|
|
63
|
+
'<span class="label nb-color-block" style="background-color: #{}"> </span>', self.color
|
|
64
64
|
)
|
|
65
65
|
return helpers.placeholder(self.color)
|
nautobot/core/models/ordering.py
CHANGED
|
@@ -28,9 +28,10 @@ def naturalize(value, max_length, integer_places=8):
|
|
|
28
28
|
location00000010router00000004
|
|
29
29
|
location00000010router00000019
|
|
30
30
|
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
Args:
|
|
32
|
+
value (str): The value to be naturalized
|
|
33
|
+
max_length (int): The maximum length of the returned string. Characters beyond this length will be stripped.
|
|
34
|
+
integer_places (int): The number of places to which each integer will be expanded. (Default: 8)
|
|
34
35
|
"""
|
|
35
36
|
if not value:
|
|
36
37
|
return value
|
|
@@ -50,8 +51,9 @@ def naturalize_interface(value, max_length):
|
|
|
50
51
|
Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old
|
|
51
52
|
InterfaceManager.
|
|
52
53
|
|
|
53
|
-
:
|
|
54
|
-
|
|
54
|
+
Args:
|
|
55
|
+
value (str): The value to be naturalized
|
|
56
|
+
max_length (int): The maximum length of the returned string. Characters beyond this length will be stripped.
|
|
55
57
|
"""
|
|
56
58
|
output = ""
|
|
57
59
|
match = re.search(INTERFACE_NAME_REGEX, value)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import ClassVar
|
|
2
|
+
|
|
1
3
|
from django.db.models import Count, OuterRef, Q, QuerySet, Subquery
|
|
2
4
|
from django.db.models.functions import Coalesce
|
|
3
5
|
|
|
@@ -112,8 +114,9 @@ class RestrictedQuerySet(CompositeKeyQuerySetMixin, QuerySet):
|
|
|
112
114
|
Filter the QuerySet to return only objects on which the specified user has been granted the specified
|
|
113
115
|
permission.
|
|
114
116
|
|
|
115
|
-
:
|
|
116
|
-
|
|
117
|
+
Args:
|
|
118
|
+
user (User): User instance
|
|
119
|
+
action (str): The action which must be permitted (e.g. "view" for "dcim.view_location"); default is 'view'
|
|
117
120
|
"""
|
|
118
121
|
# Resolve the full name of the required permission
|
|
119
122
|
app_label = self.model._meta.app_label
|
|
@@ -185,3 +188,75 @@ class RestrictedQuerySet(CompositeKeyQuerySetMixin, QuerySet):
|
|
|
185
188
|
|
|
186
189
|
"""
|
|
187
190
|
return self.order_by().values_list(*fields, flat=flat, named=named).distinct()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class BaseManyToManyQuerySetMixin:
|
|
194
|
+
"""
|
|
195
|
+
Base mixin to provide backward compatibility for fields that have been changed from ForeignKey to ManyToManyField.
|
|
196
|
+
|
|
197
|
+
Subclasses should define FIELD_MAP as a dictionary of field mappings, where the key is the old field name
|
|
198
|
+
and the value is the new field name.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
FIELD_MAP: ClassVar[dict[str, str]] = {}
|
|
202
|
+
|
|
203
|
+
def __init_subclass__(cls, **kwargs):
|
|
204
|
+
"""Combine FIELD_MAP from all parent classes into a single dictionary."""
|
|
205
|
+
super().__init_subclass__(**kwargs)
|
|
206
|
+
combined_field_map = {}
|
|
207
|
+
for base in reversed(cls.__mro__):
|
|
208
|
+
if hasattr(base, "FIELD_MAP") and isinstance(getattr(base, "FIELD_MAP"), dict):
|
|
209
|
+
combined_field_map.update(base.FIELD_MAP)
|
|
210
|
+
cls.FIELD_MAP = combined_field_map
|
|
211
|
+
|
|
212
|
+
def _convert_to_m2m_field(self, kwargs):
|
|
213
|
+
field_mappings = self.FIELD_MAP
|
|
214
|
+
if not field_mappings:
|
|
215
|
+
return kwargs
|
|
216
|
+
|
|
217
|
+
updated_kwargs = {}
|
|
218
|
+
|
|
219
|
+
for field, value in kwargs.items():
|
|
220
|
+
converted = False
|
|
221
|
+
|
|
222
|
+
# Check each field mapping
|
|
223
|
+
for old_field, new_field in field_mappings.items():
|
|
224
|
+
if field == old_field:
|
|
225
|
+
# Direct field query becomes __in for ManyToMany
|
|
226
|
+
updated_kwargs[f"{new_field}__in"] = [value]
|
|
227
|
+
converted = True
|
|
228
|
+
break
|
|
229
|
+
elif field.startswith(f"{old_field}__"):
|
|
230
|
+
# Replace old field prefix with new field prefix
|
|
231
|
+
updated_kwargs[field.replace(old_field, new_field, 1)] = value
|
|
232
|
+
converted = True
|
|
233
|
+
break
|
|
234
|
+
|
|
235
|
+
if not converted:
|
|
236
|
+
updated_kwargs[field] = value
|
|
237
|
+
|
|
238
|
+
return updated_kwargs
|
|
239
|
+
|
|
240
|
+
def filter(self, *args, **kwargs):
|
|
241
|
+
kwargs = self._convert_to_m2m_field(kwargs)
|
|
242
|
+
return super().filter(*args, **kwargs)
|
|
243
|
+
|
|
244
|
+
def exclude(self, *args, **kwargs):
|
|
245
|
+
kwargs = self._convert_to_m2m_field(kwargs)
|
|
246
|
+
return super().exclude(*args, **kwargs)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class LocationToLocationsQuerySetMixin(BaseManyToManyQuerySetMixin):
|
|
250
|
+
"""
|
|
251
|
+
Mixin to convert 'location' to 'locations' in queryset parameters.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
FIELD_MAP = {"location": "locations"}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ClusterToClustersQuerySetMixin(BaseManyToManyQuerySetMixin):
|
|
258
|
+
"""
|
|
259
|
+
Mixin to convert 'cluster' to 'clusters' in queryset parameters.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
FIELD_MAP = {"cluster": "clusters"}
|
|
@@ -9,6 +9,7 @@ from tree_queries.query import TreeManager as TreeManager_, TreeQuerySet as Tree
|
|
|
9
9
|
|
|
10
10
|
from nautobot.core.models import BaseManager, querysets
|
|
11
11
|
from nautobot.core.signals import invalidate_max_depth_cache
|
|
12
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
|
|
@@ -94,7 +95,7 @@ class TreeManager(TreeManager_, BaseManager.from_queryset(TreeQuerySet)):
|
|
|
94
95
|
|
|
95
96
|
@property
|
|
96
97
|
def max_depth_cache_key(self):
|
|
97
|
-
return
|
|
98
|
+
return construct_cache_key(self, method_name="max_depth", branch_aware=True)
|
|
98
99
|
|
|
99
100
|
@property
|
|
100
101
|
def max_depth(self):
|
|
@@ -102,10 +103,11 @@ class TreeManager(TreeManager_, BaseManager.from_queryset(TreeQuerySet)):
|
|
|
102
103
|
|
|
103
104
|
Generally TreeManagers are persistent objects while TreeQuerySets are not, hence the difference in behavior.
|
|
104
105
|
"""
|
|
105
|
-
|
|
106
|
+
cache_key = self.max_depth_cache_key
|
|
107
|
+
max_depth = cache.get(cache_key)
|
|
106
108
|
if max_depth is None:
|
|
107
109
|
max_depth = self.max_tree_depth()
|
|
108
|
-
cache.set(
|
|
110
|
+
cache.set(cache_key, max_depth)
|
|
109
111
|
return max_depth
|
|
110
112
|
|
|
111
113
|
|
|
@@ -128,7 +130,7 @@ class TreeModel(TreeNode):
|
|
|
128
130
|
"""
|
|
129
131
|
if not hasattr(self, "name"):
|
|
130
132
|
raise NotImplementedError("default TreeModel.display implementation requires a `name` attribute!")
|
|
131
|
-
cache_key =
|
|
133
|
+
cache_key = construct_cache_key(self, method_name="display", branch_aware=True)
|
|
132
134
|
display_str = cache.get(cache_key, "")
|
|
133
135
|
if display_str:
|
|
134
136
|
return display_str
|