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/constants.py
CHANGED
|
@@ -116,6 +116,11 @@ PAGINATE_COUNT_DEFAULT = 50
|
|
|
116
116
|
# Models excluded from the global search list
|
|
117
117
|
GLOBAL_SEARCH_EXCLUDE_LIST = [
|
|
118
118
|
"anotherexamplemodel",
|
|
119
|
+
"approvalworkflow",
|
|
120
|
+
"approvalworkflowdefinition",
|
|
121
|
+
"approvalworkflowstage",
|
|
122
|
+
"approvalworkflowstagedefinition",
|
|
123
|
+
"approvalworkflowstageresponse",
|
|
119
124
|
"cablepath",
|
|
120
125
|
"circuittermination",
|
|
121
126
|
"circuittype",
|
|
@@ -135,6 +140,7 @@ GLOBAL_SEARCH_EXCLUDE_LIST = [
|
|
|
135
140
|
"customlink",
|
|
136
141
|
"devicebay",
|
|
137
142
|
"devicebaytemplate",
|
|
143
|
+
"deviceclusterassignment",
|
|
138
144
|
"devicetypetosoftwareimagefile",
|
|
139
145
|
"dynamicgroupmembership",
|
|
140
146
|
"exporttemplate",
|
|
@@ -204,3 +210,6 @@ GLOBAL_SEARCH_EXCLUDE_LIST = [
|
|
|
204
210
|
"vrfprefixassignment",
|
|
205
211
|
"webhook",
|
|
206
212
|
]
|
|
213
|
+
|
|
214
|
+
# M2M fields that are included by default in API responses if exclude_m2m is not provided
|
|
215
|
+
DEFAULT_M2M_FIELDS = ["tags", "content_types", "object_types"]
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
from urllib.parse import urlparse
|
|
2
|
+
|
|
1
3
|
from django.conf import settings as django_settings
|
|
4
|
+
from django.urls import NoReverseMatch, reverse
|
|
2
5
|
|
|
3
6
|
from nautobot.core.settings_funcs import sso_auth_enabled
|
|
7
|
+
from nautobot.core.templatetags.helpers import has_one_or_more_perms
|
|
8
|
+
from nautobot.core.utils import lookup
|
|
9
|
+
from nautobot.extras.registry import registry
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
def get_saml_idp():
|
|
@@ -40,6 +46,84 @@ def settings(request):
|
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
|
|
49
|
+
def nav_menu(request):
|
|
50
|
+
"""
|
|
51
|
+
Expose nav menu data for navigation and global search.
|
|
52
|
+
Also, indicate whether `"nautobot_version_control"` app is installed in order to render branch picker in nav menu.
|
|
53
|
+
"""
|
|
54
|
+
has_identified_active_link = False
|
|
55
|
+
related_list_view_link = None
|
|
56
|
+
if request.resolver_match:
|
|
57
|
+
# Try to map requested page `view_name` to a specific `model` via `lookup.get_model_for_view_name`.
|
|
58
|
+
try:
|
|
59
|
+
model = lookup.get_model_for_view_name(request.resolver_match.view_name)
|
|
60
|
+
except ValueError:
|
|
61
|
+
model = None
|
|
62
|
+
|
|
63
|
+
# If model mapping above fails, fall back to deriving a `model` from requested page `view_class` `queryset`.
|
|
64
|
+
if not model:
|
|
65
|
+
view_func = request.resolver_match.func
|
|
66
|
+
view_class = None
|
|
67
|
+
if hasattr(view_func, "view_class"): # Valid for generic Views
|
|
68
|
+
view_class = view_func.view_class
|
|
69
|
+
elif hasattr(view_func, "cls"): # Valid for UI component framework ViewSets
|
|
70
|
+
view_class = view_func.cls
|
|
71
|
+
view_instance = view_class() if view_class else None
|
|
72
|
+
queryset = getattr(view_instance, "queryset", None)
|
|
73
|
+
model = getattr(queryset, "model", None)
|
|
74
|
+
|
|
75
|
+
# If related `model` reference has been found, map it to a list view link.
|
|
76
|
+
try:
|
|
77
|
+
related_list_view_name = lookup.get_route_for_model(model, "list") if model else None
|
|
78
|
+
related_list_view_link = reverse(related_list_view_name) if related_list_view_name else None
|
|
79
|
+
except (NoReverseMatch, ValueError):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
nav_menu_object = {"tabs": {}}
|
|
83
|
+
|
|
84
|
+
if htmx_current_url := request.headers.get("HX-Current-URL"):
|
|
85
|
+
current_url = urlparse(htmx_current_url).path
|
|
86
|
+
else:
|
|
87
|
+
current_url = request.path
|
|
88
|
+
|
|
89
|
+
for tab_name, tab_details in registry["nav_menu"]["tabs"].items():
|
|
90
|
+
if not tab_details["permissions"] or has_one_or_more_perms(request.user, tab_details["permissions"]):
|
|
91
|
+
nav_menu_object["tabs"][tab_name] = {"groups": {}, "icon": tab_details["icon"]}
|
|
92
|
+
for group_name, group_details in tab_details["groups"].items():
|
|
93
|
+
if not group_details["permissions"] or has_one_or_more_perms(
|
|
94
|
+
request.user, group_details["permissions"]
|
|
95
|
+
):
|
|
96
|
+
nav_menu_object["tabs"][tab_name]["groups"][group_name] = {"items": {}}
|
|
97
|
+
for item_link, item_details in group_details["items"].items():
|
|
98
|
+
if not item_details["permissions"] or has_one_or_more_perms(
|
|
99
|
+
request.user, item_details["permissions"]
|
|
100
|
+
):
|
|
101
|
+
if has_identified_active_link:
|
|
102
|
+
is_active = False
|
|
103
|
+
else:
|
|
104
|
+
is_active = item_link in [current_url, related_list_view_link]
|
|
105
|
+
if is_active:
|
|
106
|
+
has_identified_active_link = True
|
|
107
|
+
|
|
108
|
+
nav_menu_object["tabs"][tab_name]["groups"][group_name]["items"][item_link] = {
|
|
109
|
+
"is_active": is_active,
|
|
110
|
+
"name": item_details["name"],
|
|
111
|
+
"weight": item_details["weight"],
|
|
112
|
+
}
|
|
113
|
+
if len(nav_menu_object["tabs"][tab_name]["groups"][group_name]["items"]) == 0:
|
|
114
|
+
del nav_menu_object["tabs"][tab_name]["groups"][group_name]
|
|
115
|
+
if len(nav_menu_object["tabs"][tab_name]["groups"]) == 0:
|
|
116
|
+
del nav_menu_object["tabs"][tab_name]
|
|
117
|
+
|
|
118
|
+
nav_menu_version_control = None
|
|
119
|
+
if "nautobot_version_control" in django_settings.PLUGINS:
|
|
120
|
+
from nautobot_version_control.utils import active_branch # pylint: disable=import-error
|
|
121
|
+
|
|
122
|
+
nav_menu_version_control = {"active_branch": active_branch()}
|
|
123
|
+
|
|
124
|
+
return {"nav_menu": nav_menu_object, "nav_menu_version_control": nav_menu_version_control}
|
|
125
|
+
|
|
126
|
+
|
|
43
127
|
def sso_auth(request):
|
|
44
128
|
"""
|
|
45
129
|
Expose SSO-related variables for use in generating login URL fragments for external authentication providers.
|
nautobot/core/filters.py
CHANGED
|
@@ -5,12 +5,22 @@ import uuid
|
|
|
5
5
|
from django import forms as django_forms
|
|
6
6
|
from django.conf import settings
|
|
7
7
|
from django.db import models
|
|
8
|
+
from django.db.models.constants import LOOKUP_SEP
|
|
9
|
+
from django.db.models.fields.related import ManyToManyRel, ManyToOneRel, OneToOneRel
|
|
8
10
|
from django.forms.utils import ErrorDict, ErrorList
|
|
9
11
|
from django.utils.encoding import force_str
|
|
10
12
|
from django.utils.text import capfirst
|
|
11
13
|
import django_filters
|
|
12
14
|
from django_filters.constants import EMPTY_VALUES
|
|
13
|
-
from django_filters.
|
|
15
|
+
from django_filters.filterset import remote_queryset
|
|
16
|
+
from django_filters.utils import (
|
|
17
|
+
get_field_parts,
|
|
18
|
+
get_model_field,
|
|
19
|
+
label_for_filter,
|
|
20
|
+
resolve_field,
|
|
21
|
+
verbose_field_name,
|
|
22
|
+
verbose_lookup_expr,
|
|
23
|
+
)
|
|
14
24
|
from drf_spectacular.types import OpenApiTypes
|
|
15
25
|
from drf_spectacular.utils import extend_schema_field
|
|
16
26
|
import timezone_field
|
|
@@ -400,9 +410,75 @@ class MappedPredicatesFilterMixin:
|
|
|
400
410
|
return qs.distinct()
|
|
401
411
|
|
|
402
412
|
|
|
413
|
+
class ModelMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
|
|
414
|
+
"""Subclass of the django-filters class by the same name with an improved default `label` formulation."""
|
|
415
|
+
|
|
416
|
+
def __init__(self, *args, **kwargs):
|
|
417
|
+
if "to_field_name" in kwargs:
|
|
418
|
+
self.to_field_name = kwargs["to_field_name"]
|
|
419
|
+
super().__init__(*args, **kwargs)
|
|
420
|
+
|
|
421
|
+
@property
|
|
422
|
+
def to_field_name_label(self):
|
|
423
|
+
if hasattr(self, "to_field_name") and hasattr(self, "model") and self.to_field_name != "id":
|
|
424
|
+
field_name = self.field_name
|
|
425
|
+
if field_name.endswith(f"__{self.to_field_name}"): # e.g. field_name = "device__name", to_field_name="name"
|
|
426
|
+
field_name = LOOKUP_SEP.join(field_name.split(LOOKUP_SEP)[:-1])
|
|
427
|
+
to_field_name = LOOKUP_SEP.join([field_name, self.to_field_name])
|
|
428
|
+
field_parts = get_field_parts(self.model, to_field_name) # pylint: disable=no-member
|
|
429
|
+
if field_parts:
|
|
430
|
+
return field_parts[-1].verbose_name
|
|
431
|
+
return self.to_field_name
|
|
432
|
+
return "ID"
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def label(self): # pylint: disable=arguments-differ,invalid-overridden-method
|
|
436
|
+
"""
|
|
437
|
+
Override django_filters.Filter.label property to generate a more useful default label.
|
|
438
|
+
|
|
439
|
+
Examples:
|
|
440
|
+
>>> import django_filters
|
|
441
|
+
>>> from nautobot.core.filters import BaseFilterSet, ModelMultipleChoiceFilter
|
|
442
|
+
>>> class DemoFilterSet(BaseFilterSet):
|
|
443
|
+
... class Meta:
|
|
444
|
+
... model = Interface
|
|
445
|
+
... fields = []
|
|
446
|
+
... old_device = django_filters.ModelMultipleChoiceFilter(queryset=Device.objects.all(), field_name="device")
|
|
447
|
+
... new_device = ModelMultipleChoiceFilter(queryset=Device.objects.all(), field_name="device")
|
|
448
|
+
... device_name = ModelMultipleChoiceFilter(queryset=Device.objects.all(), field_name="device", to_field_name="name")
|
|
449
|
+
...
|
|
450
|
+
>>> DemoFilterSet().filters["old_device"].label
|
|
451
|
+
'Device'
|
|
452
|
+
>>> DemoFilterSet().filters["new_device"].label
|
|
453
|
+
'Device (ID)'
|
|
454
|
+
>>> DemoFilterSet().filters["device_name"].label
|
|
455
|
+
'Device (Name)'
|
|
456
|
+
"""
|
|
457
|
+
if self._label is None and hasattr(self, "model"):
|
|
458
|
+
name = verbose_field_name(self.model, self.field_name) # pylint: disable=no-member
|
|
459
|
+
if name == "[invalid name]":
|
|
460
|
+
name = self.field_name
|
|
461
|
+
verbose_expression = ["exclude", name] if self.exclude else [name]
|
|
462
|
+
|
|
463
|
+
# Nautobot-specific enhancement
|
|
464
|
+
verbose_expression.append(f"({self.to_field_name_label})")
|
|
465
|
+
|
|
466
|
+
# iterable lookups indicate a LookupTypeField, which should not be verbose
|
|
467
|
+
if isinstance(self.lookup_expr, str):
|
|
468
|
+
verbose_expression.append(verbose_lookup_expr(self.lookup_expr))
|
|
469
|
+
|
|
470
|
+
verbose_expression = [force_str(part) for part in verbose_expression if part]
|
|
471
|
+
self._label = capfirst(" ".join(verbose_expression))
|
|
472
|
+
return self._label
|
|
473
|
+
|
|
474
|
+
@label.setter
|
|
475
|
+
def label(self, value): # pylint: disable=invalid-overridden-method
|
|
476
|
+
self._label = value
|
|
477
|
+
|
|
478
|
+
|
|
403
479
|
# TODO(timizuo): NaturalKeyOrPKMultipleChoiceFilter is not currently handling pk Integer field properly; resolve this in issue #3336
|
|
404
480
|
@extend_schema_field(OpenApiTypes.STR)
|
|
405
|
-
class NaturalKeyOrPKMultipleChoiceFilter(
|
|
481
|
+
class NaturalKeyOrPKMultipleChoiceFilter(ModelMultipleChoiceFilter):
|
|
406
482
|
"""
|
|
407
483
|
Filter that supports filtering on values matching the `pk` field and another
|
|
408
484
|
field of a foreign-key related object. The desired field is set using the `to_field_name`
|
|
@@ -425,6 +501,17 @@ class NaturalKeyOrPKMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilte
|
|
|
425
501
|
self.prefers_id = prefers_id
|
|
426
502
|
super().__init__(*args, **kwargs)
|
|
427
503
|
|
|
504
|
+
@property
|
|
505
|
+
def to_field_name_label(self):
|
|
506
|
+
"""
|
|
507
|
+
Override ModelMultipleChoiceFilter.to_field_name_label to indicate both field_name options.
|
|
508
|
+
|
|
509
|
+
Examples:
|
|
510
|
+
>>> VirtualMachineFilterSet().filters["software_image_files"].label
|
|
511
|
+
'Software Image Files (Image File Name or ID)'
|
|
512
|
+
"""
|
|
513
|
+
return f"{super().to_field_name_label} or ID"
|
|
514
|
+
|
|
428
515
|
def get_filter_predicate(self, v):
|
|
429
516
|
"""
|
|
430
517
|
Override base filter behavior to force the filter to use the `pk` field instead of
|
|
@@ -574,9 +661,32 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
574
661
|
models.DecimalField: {"filter_class": MultiValueDecimalFilter},
|
|
575
662
|
models.EmailField: {"filter_class": MultiValueCharFilter},
|
|
576
663
|
models.FloatField: {"filter_class": MultiValueFloatFilter},
|
|
664
|
+
# TODO: should be NaturalKeyOrPKMultipleChoiceFilter but not all models have a "name" or other natural key
|
|
665
|
+
models.ForeignKey: {
|
|
666
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
667
|
+
"extra": lambda field: {
|
|
668
|
+
"null_label": django_filters.conf.settings.NULL_CHOICE_LABEL if field.null else None,
|
|
669
|
+
"queryset": remote_queryset(field),
|
|
670
|
+
"to_field_name": field.remote_field.field_name,
|
|
671
|
+
},
|
|
672
|
+
},
|
|
577
673
|
models.IntegerField: {"filter_class": MultiValueNumberFilter},
|
|
578
674
|
# Ref: https://github.com/carltongibson/django-filter/issues/1107
|
|
579
675
|
models.JSONField: {"filter_class": MultiValueCharFilter, "extra": lambda f: {"lookup_expr": "icontains"}},
|
|
676
|
+
models.ManyToManyField: {
|
|
677
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
678
|
+
"extra": lambda f: {
|
|
679
|
+
"queryset": remote_queryset(f),
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
models.OneToOneField: {
|
|
683
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
684
|
+
"extra": lambda f: {
|
|
685
|
+
"queryset": remote_queryset(f),
|
|
686
|
+
"to_field_name": f.remote_field.field_name,
|
|
687
|
+
"null_label": django_filters.conf.settings.NULL_CHOICE_LABEL if f.null else None,
|
|
688
|
+
},
|
|
689
|
+
},
|
|
580
690
|
models.PositiveIntegerField: {"filter_class": MultiValueNumberFilter},
|
|
581
691
|
models.PositiveSmallIntegerField: {"filter_class": MultiValueNumberFilter},
|
|
582
692
|
models.SlugField: {"filter_class": MultiValueCharFilter},
|
|
@@ -585,6 +695,25 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
585
695
|
models.TimeField: {"filter_class": MultiValueTimeFilter},
|
|
586
696
|
models.URLField: {"filter_class": MultiValueCharFilter},
|
|
587
697
|
models.UUIDField: {"filter_class": MultiValueUUIDFilter},
|
|
698
|
+
ManyToManyRel: {
|
|
699
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
700
|
+
"extra": lambda f: {
|
|
701
|
+
"queryset": remote_queryset(f),
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
ManyToOneRel: {
|
|
705
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
706
|
+
"extra": lambda f: {
|
|
707
|
+
"queryset": remote_queryset(f),
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
OneToOneRel: {
|
|
711
|
+
"filter_class": ModelMultipleChoiceFilter,
|
|
712
|
+
"extra": lambda f: {
|
|
713
|
+
"queryset": remote_queryset(f),
|
|
714
|
+
"null_label": django_filters.conf.settings.NULL_CHOICE_LABEL if f.null else None,
|
|
715
|
+
},
|
|
716
|
+
},
|
|
588
717
|
core_fields.MACAddressCharField: {"filter_class": MultiValueMACAddressFilter},
|
|
589
718
|
core_fields.TagsField: {"filter_class": TagFilter},
|
|
590
719
|
timezone_field.TimeZoneField: {"filter_class": MultiValueCharFilter},
|
nautobot/core/forms/__init__.py
CHANGED
|
@@ -35,6 +35,7 @@ from nautobot.core.forms.fields import (
|
|
|
35
35
|
)
|
|
36
36
|
from nautobot.core.forms.forms import (
|
|
37
37
|
AddressFieldMixin,
|
|
38
|
+
ApprovalForm,
|
|
38
39
|
BootstrapMixin,
|
|
39
40
|
BulkEditForm,
|
|
40
41
|
BulkRenameForm,
|
|
@@ -46,7 +47,7 @@ from nautobot.core.forms.forms import (
|
|
|
46
47
|
ReturnURLForm,
|
|
47
48
|
TableConfigForm,
|
|
48
49
|
)
|
|
49
|
-
from nautobot.core.forms.search import
|
|
50
|
+
from nautobot.core.forms.search import search_model_choices
|
|
50
51
|
from nautobot.core.forms.utils import (
|
|
51
52
|
add_blank_choice,
|
|
52
53
|
add_field_to_filter_form_class,
|
|
@@ -87,6 +88,7 @@ __all__ = (
|
|
|
87
88
|
"APISelect",
|
|
88
89
|
"APISelectMultiple",
|
|
89
90
|
"AddressFieldMixin",
|
|
91
|
+
"ApprovalForm",
|
|
90
92
|
"AutoPopulateWidget",
|
|
91
93
|
"AutoPositionField",
|
|
92
94
|
"AutoPositionPatternField",
|
|
@@ -127,7 +129,6 @@ __all__ = (
|
|
|
127
129
|
"NumericArrayField",
|
|
128
130
|
"PrefixFieldMixin",
|
|
129
131
|
"ReturnURLForm",
|
|
130
|
-
"SearchForm",
|
|
131
132
|
"SelectWithDisabled",
|
|
132
133
|
"SelectWithPK",
|
|
133
134
|
"SlugField",
|
|
@@ -146,4 +147,5 @@ __all__ = (
|
|
|
146
147
|
"parse_alphanumeric_range",
|
|
147
148
|
"parse_numeric_range",
|
|
148
149
|
"restrict_form_fields",
|
|
150
|
+
"search_model_choices",
|
|
149
151
|
)
|
nautobot/core/forms/fields.py
CHANGED
|
@@ -482,13 +482,14 @@ class AutoPositionPatternField(ExpandableNameField):
|
|
|
482
482
|
|
|
483
483
|
class DynamicModelChoiceMixin:
|
|
484
484
|
"""
|
|
485
|
-
:
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
485
|
+
Args:
|
|
486
|
+
display_field (str): The name of the attribute of an API response object to display in the selection list
|
|
487
|
+
query_params (Optional[dict]): Additional key/value pairs to attach to the API request
|
|
488
|
+
initial_params (Optional[dict]): Child field references to use for selecting a parent field's initial value
|
|
489
|
+
null_option (Optional[str]): The string used to represent a null selection (if any)
|
|
490
|
+
disabled_indicator (Optional[str]): The name of the field which, if populated, will disable selection of the
|
|
491
|
+
choice
|
|
492
|
+
depth (int): Nested serialization depth when making API requests (default: `0` or a flat representation)
|
|
492
493
|
"""
|
|
493
494
|
|
|
494
495
|
filter = django_filters.ModelChoiceFilter # 2.0 TODO(Glenn): can we rename this? pylint: disable=redefined-builtin
|
|
@@ -886,7 +887,8 @@ class TagFilterField(DynamicModelMultipleChoiceField):
|
|
|
886
887
|
"""
|
|
887
888
|
A filter field for the tags of a model. Only the tags used by a model are displayed.
|
|
888
889
|
|
|
889
|
-
:
|
|
890
|
+
Args:
|
|
891
|
+
model (Model): The model of the filter
|
|
890
892
|
"""
|
|
891
893
|
|
|
892
894
|
def __init__(self, model, *args, query_params=None, queryset=None, **kwargs):
|
nautobot/core/forms/forms.py
CHANGED
|
@@ -5,12 +5,13 @@ import re
|
|
|
5
5
|
|
|
6
6
|
from django import forms
|
|
7
7
|
from django.core.exceptions import FieldDoesNotExist
|
|
8
|
-
from django.db.models.fields.related import ManyToManyField
|
|
8
|
+
from django.db.models.fields.related import ManyToManyField, ManyToManyRel
|
|
9
9
|
from django.forms import formset_factory
|
|
10
10
|
from django.urls import reverse
|
|
11
11
|
import yaml
|
|
12
12
|
|
|
13
13
|
from nautobot.core.forms import widgets as nautobot_widgets
|
|
14
|
+
from nautobot.core.forms.fields import CommentField
|
|
14
15
|
from nautobot.core.utils.filtering import build_lookup_label, get_filter_field_label, get_filterset_parameter_form_field
|
|
15
16
|
from nautobot.ipam import formfields
|
|
16
17
|
|
|
@@ -87,6 +88,10 @@ class BootstrapMixin(forms.BaseForm):
|
|
|
87
88
|
css_classes = field.widget.attrs.get("class", "")
|
|
88
89
|
if "form-control" not in css_classes:
|
|
89
90
|
field.widget.attrs["class"] = " ".join([css_classes, "form-control"]).strip()
|
|
91
|
+
if isinstance(field.widget, (forms.CheckboxInput, forms.RadioSelect)):
|
|
92
|
+
css_classes = field.widget.attrs.get("class", "")
|
|
93
|
+
if "form-check" not in css_classes:
|
|
94
|
+
field.widget.attrs["class"] = " ".join([css_classes, "form-check-input"]).strip()
|
|
90
95
|
if field.required and not isinstance(field.widget, forms.FileInput):
|
|
91
96
|
field.widget.attrs["required"] = "required"
|
|
92
97
|
if "placeholder" not in field.widget.attrs:
|
|
@@ -109,6 +114,15 @@ class ConfirmationForm(BootstrapMixin, ReturnURLForm):
|
|
|
109
114
|
confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
|
|
110
115
|
|
|
111
116
|
|
|
117
|
+
class ApprovalForm(BootstrapMixin, ReturnURLForm):
|
|
118
|
+
"""
|
|
119
|
+
A generic comment form. The form is not valid unless the confirm field is checked.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
comments = CommentField(label="Comments", required=False)
|
|
123
|
+
confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
|
|
124
|
+
|
|
125
|
+
|
|
112
126
|
class BulkEditForm(forms.Form):
|
|
113
127
|
"""
|
|
114
128
|
Base form for editing multiple objects in bulk.
|
|
@@ -146,7 +160,7 @@ class BulkEditForm(forms.Form):
|
|
|
146
160
|
continue
|
|
147
161
|
with contextlib.suppress(FieldDoesNotExist):
|
|
148
162
|
field = obj._meta.get_field(field_name)
|
|
149
|
-
is_m2m_field = isinstance(field, (ManyToManyField, TagsField))
|
|
163
|
+
is_m2m_field = isinstance(field, (ManyToManyField, ManyToManyRel, TagsField))
|
|
150
164
|
if is_m2m_field:
|
|
151
165
|
m2m_field_names.append(field_name)
|
|
152
166
|
|
|
@@ -273,7 +287,7 @@ class TableConfigForm(BootstrapMixin, forms.Form):
|
|
|
273
287
|
columns = forms.MultipleChoiceField(
|
|
274
288
|
choices=[],
|
|
275
289
|
required=False,
|
|
276
|
-
widget=forms.SelectMultiple(attrs={"size":
|
|
290
|
+
widget=forms.SelectMultiple(attrs={"size": 20}),
|
|
277
291
|
help_text="Use the buttons below to arrange columns in the desired order, then select all columns to display.",
|
|
278
292
|
)
|
|
279
293
|
|
|
@@ -363,7 +377,7 @@ class DynamicFilterForm(BootstrapMixin, forms.Form):
|
|
|
363
377
|
|
|
364
378
|
def _get_lookup_field_choices(self):
|
|
365
379
|
"""Get choices for lookup_fields i.e filterset parameters without a lookup expr"""
|
|
366
|
-
from nautobot.extras.
|
|
380
|
+
from nautobot.extras.filter_mixins import RelationshipFilter # Avoid circular import
|
|
367
381
|
|
|
368
382
|
filterset_without_lookup = (
|
|
369
383
|
(
|
|
@@ -381,14 +395,12 @@ def dynamic_formset_factory(filterset, data=None, **kwargs):
|
|
|
381
395
|
filter_form.filterset = filterset
|
|
382
396
|
|
|
383
397
|
params = {
|
|
384
|
-
"can_delete_extra":
|
|
385
|
-
"can_delete":
|
|
386
|
-
"extra":
|
|
398
|
+
"can_delete_extra": False,
|
|
399
|
+
"can_delete": False,
|
|
400
|
+
"extra": 1,
|
|
387
401
|
}
|
|
388
402
|
kwargs.update(params)
|
|
389
403
|
form = formset_factory(form=filter_form, **kwargs)
|
|
390
|
-
if data:
|
|
391
|
-
form = form(data=data)
|
|
392
404
|
|
|
393
405
|
return form
|
|
394
406
|
|
nautobot/core/forms/search.py
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
from django import forms
|
|
2
1
|
from django.apps import apps
|
|
3
2
|
|
|
4
|
-
from nautobot.core.forms import BootstrapMixin
|
|
5
|
-
|
|
6
3
|
|
|
7
4
|
def search_model_choices():
|
|
8
5
|
"""
|
|
@@ -19,15 +16,3 @@ def search_model_choices():
|
|
|
19
16
|
]
|
|
20
17
|
choices.append((app_label, model_tuples))
|
|
21
18
|
return choices
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class SearchForm(BootstrapMixin, forms.Form):
|
|
25
|
-
q = forms.CharField(label="Search")
|
|
26
|
-
|
|
27
|
-
obj_type = forms.ChoiceField(choices=search_model_choices, required=False, label="Type")
|
|
28
|
-
|
|
29
|
-
def __init__(self, *args, q_placeholder=None, **kwargs):
|
|
30
|
-
super().__init__(*args, **kwargs)
|
|
31
|
-
|
|
32
|
-
if q_placeholder:
|
|
33
|
-
self.fields["q"].widget.attrs["placeholder"] = q_placeholder
|
nautobot/core/forms/widgets.py
CHANGED
|
@@ -178,8 +178,9 @@ class APISelect(SelectWithDisabled):
|
|
|
178
178
|
"""
|
|
179
179
|
Add details for an additional query param in the form of a data-* JSON-encoded list attribute.
|
|
180
180
|
|
|
181
|
-
:
|
|
182
|
-
|
|
181
|
+
Args:
|
|
182
|
+
name (str): The name of the query param
|
|
183
|
+
value (Any): The value of the query param
|
|
183
184
|
"""
|
|
184
185
|
key = f"data-query-param-{name}"
|
|
185
186
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from django.test.client import RequestFactory
|
|
2
|
-
from graphene.types import
|
|
2
|
+
from graphene.types import BigInt
|
|
3
3
|
from graphene_django.settings import graphene_settings
|
|
4
|
-
from graphql import
|
|
5
|
-
from graphql.language import ast
|
|
4
|
+
from graphql import execute, parse
|
|
6
5
|
|
|
7
6
|
from nautobot.extras.models import GraphQLQuery
|
|
8
7
|
|
|
@@ -24,13 +23,12 @@ def execute_query(query, variables=None, request=None, user=None):
|
|
|
24
23
|
if not request:
|
|
25
24
|
request = RequestFactory().post("/graphql/")
|
|
26
25
|
request.user = user
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
document = backend.document_from_string(schema, query)
|
|
26
|
+
schema = graphene_settings.SCHEMA.graphql_schema
|
|
27
|
+
document = parse(query)
|
|
30
28
|
if variables:
|
|
31
|
-
return
|
|
29
|
+
return execute(schema=schema, document=document, context_value=request, variable_values=variables)
|
|
32
30
|
else:
|
|
33
|
-
return
|
|
31
|
+
return execute(schema=schema, document=document, context_value=request)
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
def execute_saved_query(saved_query_name, **kwargs):
|
|
@@ -51,21 +49,5 @@ def execute_saved_query(saved_query_name, **kwargs):
|
|
|
51
49
|
return execute_query(query=query.query, **kwargs)
|
|
52
50
|
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# https://github.com/graphql-python/graphene/pull/1261 (graphene 3.0)
|
|
57
|
-
class BigInteger(Scalar):
|
|
58
|
-
"""An integer which, unlike GraphQL's native Int type, doesn't reject values outside (-2^31, 2^31-1).
|
|
59
|
-
|
|
60
|
-
Currently only used for ASNField, which goes up to 2^32-1 (i.e., unsigned 32-bit int); it's possible
|
|
61
|
-
that this approach may fail for values in excess of 2^53-1 (the largest integer value supported in JavaScript).
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
serialize = int
|
|
65
|
-
parse_value = int
|
|
66
|
-
|
|
67
|
-
@staticmethod
|
|
68
|
-
def parse_literal(node):
|
|
69
|
-
if isinstance(node, ast.IntValue):
|
|
70
|
-
return int(node.value)
|
|
71
|
-
return None
|
|
52
|
+
class BigInteger(BigInt):
|
|
53
|
+
"""For backwards compatibility only."""
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
import graphene
|
|
6
|
+
from graphene_django.fields import DjangoListField
|
|
6
7
|
import graphene_django_optimizer as gql_optimizer
|
|
7
8
|
from graphql import GraphQLError
|
|
8
9
|
|
|
@@ -27,7 +28,7 @@ def generate_restricted_queryset():
|
|
|
27
28
|
fail gracefully in that case.
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
def get_queryset(queryset, info):
|
|
31
|
+
def get_queryset(cls, queryset, info):
|
|
31
32
|
if not hasattr(queryset, "restrict"):
|
|
32
33
|
logger.debug(f"Queryset {queryset} is not restrictable")
|
|
33
34
|
return queryset
|
|
@@ -74,9 +75,11 @@ def generate_filter_resolver(schema_type, resolver_name, field_name):
|
|
|
74
75
|
if not filterset_class or not kwargs:
|
|
75
76
|
return field.all()
|
|
76
77
|
|
|
77
|
-
#
|
|
78
|
+
# Backwards-compatibility with Nautobot v2 - "_type" as a (now deprecated) alias for "type" filter
|
|
78
79
|
if "_type" in kwargs:
|
|
79
|
-
|
|
80
|
+
_type = kwargs.pop("_type")
|
|
81
|
+
if "type" not in kwargs:
|
|
82
|
+
kwargs["type"] = _type
|
|
80
83
|
|
|
81
84
|
resolved_obj = filterset_class(kwargs, field.all())
|
|
82
85
|
|
|
@@ -92,7 +95,7 @@ def generate_filter_resolver(schema_type, resolver_name, field_name):
|
|
|
92
95
|
errors[key] = resolved_obj.errors[key]
|
|
93
96
|
|
|
94
97
|
# Raising this exception will send the error message in the response of the GraphQL request
|
|
95
|
-
raise GraphQLError(errors)
|
|
98
|
+
raise GraphQLError(str(errors))
|
|
96
99
|
|
|
97
100
|
resolve_filter.__name__ = resolver_name
|
|
98
101
|
return resolve_filter
|
|
@@ -316,6 +319,13 @@ def generate_list_resolver(schema_type, resolver_name):
|
|
|
316
319
|
|
|
317
320
|
def list_resolver(self, info, limit=None, offset=None, **kwargs):
|
|
318
321
|
filterset_class = schema_type._meta.filterset_class
|
|
322
|
+
|
|
323
|
+
# Backwards-compatibility with Nautobot v2 - "_type" as a (now deprecated) alias for "type" filter
|
|
324
|
+
if "_type" in kwargs:
|
|
325
|
+
_type = kwargs.pop("_type")
|
|
326
|
+
if "type" not in kwargs:
|
|
327
|
+
kwargs["type"] = _type
|
|
328
|
+
|
|
319
329
|
if filterset_class is not None:
|
|
320
330
|
resolved_obj = filterset_class(kwargs, model.objects.restrict(info.context.user, "view").all())
|
|
321
331
|
|
|
@@ -329,7 +339,7 @@ def generate_list_resolver(schema_type, resolver_name):
|
|
|
329
339
|
errors[key] = resolved_obj.errors[key]
|
|
330
340
|
|
|
331
341
|
# Raising this exception will send the error message in the response of the GraphQL request
|
|
332
|
-
raise GraphQLError(errors)
|
|
342
|
+
raise GraphQLError(str(errors))
|
|
333
343
|
qs = resolved_obj.qs.all()
|
|
334
344
|
|
|
335
345
|
else:
|
|
@@ -365,7 +375,7 @@ def generate_attrs_for_schema_type(schema_type):
|
|
|
365
375
|
# Define Attributes for single item and list with their search parameters
|
|
366
376
|
search_params = generate_list_search_parameters(schema_type)
|
|
367
377
|
attrs[single_item_name] = graphene.Field(schema_type, id=graphene.ID())
|
|
368
|
-
attrs[list_name] =
|
|
378
|
+
attrs[list_name] = DjangoListField(schema_type, **search_params)
|
|
369
379
|
|
|
370
380
|
# Define Resolvers for both single item and list
|
|
371
381
|
single_item_resolver_name = f"{RESOLVER_PREFIX}{single_item_name}"
|
nautobot/core/graphql/schema.py
CHANGED
|
@@ -119,7 +119,7 @@ def extend_schema_type(schema_type):
|
|
|
119
119
|
#
|
|
120
120
|
# Queryset
|
|
121
121
|
#
|
|
122
|
-
setattr(schema_type, "get_queryset", generate_restricted_queryset())
|
|
122
|
+
setattr(schema_type, "get_queryset", classmethod(generate_restricted_queryset()))
|
|
123
123
|
|
|
124
124
|
#
|
|
125
125
|
# Custom Fields
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import graphene
|
|
2
|
-
from graphene_django.types import ObjectType
|
|
3
2
|
|
|
4
3
|
from .schema import generate_query_mixin
|
|
5
4
|
|
|
6
5
|
DynamicGraphQL = generate_query_mixin()
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
class Query(ObjectType, DynamicGraphQL):
|
|
8
|
+
class Query(graphene.ObjectType, DynamicGraphQL):
|
|
10
9
|
"""Contains the entire GraphQL Schema definition for Nautobot."""
|
|
11
10
|
|
|
12
11
|
|