nautobot 3.0.0a2__py3-none-any.whl → 3.0.0rc1__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.
- nautobot/apps/choices.py +4 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +13 -1
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +12 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
- nautobot/core/cli/migrate_deprecated_templates.py +227 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +62 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +35 -19
- nautobot/core/settings.yaml +17 -33
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +6 -7
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +22 -9
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +6 -5
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +4 -5
- nautobot/core/templates/graphene/graphiql.html +7 -8
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +11 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +3 -9
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -11
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
- nautobot/core/templates/utilities/theme_preview.html +19 -3
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +76 -18
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +41 -17
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -13
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +8 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +27 -28
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +144 -3
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +62 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +123 -4
- nautobot/core/tests/test_utils.py +72 -5
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +70 -29
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +143 -27
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +143 -55
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +20 -9
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +28 -18
- nautobot/core/utils/lookup.py +49 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +45 -22
- nautobot/core/views/renderers.py +4 -3
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +3 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +65 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
- nautobot/dcim/forms.py +90 -4
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +98 -64
- nautobot/dcim/models/devices.py +30 -20
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +18 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +15 -15
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_trace.html +0 -4
- nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
- nautobot/dcim/templates/dcim/consoleport.html +7 -6
- nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +10 -9
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +35 -7
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +6 -5
- nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
- nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
- nautobot/dcim/templates/dcim/rearport.html +8 -7
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
- nautobot/dcim/tests/integration/test_controller.py +4 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +194 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +55 -7
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +328 -4
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +132 -29
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +777 -397
- nautobot/extras/api/views.py +60 -45
- nautobot/extras/choices.py +2 -13
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +33 -48
- nautobot/extras/forms/forms.py +14 -15
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +11 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- 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/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +33 -14
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +60 -4
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +23 -2
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -9
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +64 -68
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
- nautobot/extras/templates/extras/plugin_detail.html +29 -24
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secret_create.html +1 -1
- 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 +8 -9
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +9 -10
- nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +90 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +59 -5
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +57 -22
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +51 -33
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +52 -112
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +164 -71
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +53 -38
- 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 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +19 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- 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_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +36 -1
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +44 -2
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +53 -11
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- 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/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +112 -30
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +45 -4
- nautobot/virtualization/views.py +4 -1
- 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 +219 -0
- nautobot/vpn/filters.py +234 -0
- nautobot/vpn/forms.py +487 -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 +535 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +383 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +336 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +293 -0
- nautobot/vpn/tests/test_models.py +147 -0
- nautobot/vpn/tests/test_views.py +300 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +495 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/template_content.py +0 -42
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
nautobot/core/tests/runner.py
CHANGED
|
@@ -39,11 +39,12 @@ class NautobotParallelTestSuite(ParallelTestSuite):
|
|
|
39
39
|
|
|
40
40
|
class NautobotTestRunner(DiscoverRunner):
|
|
41
41
|
"""
|
|
42
|
-
Custom test runner that excludes (slow) integration and migration tests by default.
|
|
42
|
+
Custom test runner that excludes (slow) integration and migration tests by default among others.
|
|
43
43
|
|
|
44
44
|
This test runner is aware of our use of the "integration" tag and only runs integration tests if
|
|
45
45
|
explicitly passed in with `nautobot-server test --tag integration`.
|
|
46
46
|
Similarly, it only runs migration tests if explicitly called with `--tag migration_test`.
|
|
47
|
+
Similarly, it only runs tests that require the example app(s) if those are present in settings.PLUGINS.
|
|
47
48
|
|
|
48
49
|
By Nautobot convention, integration tests must be tagged with "integration". The base
|
|
49
50
|
`nautobot.core.testing.integration.SeleniumTestCase` has this tag, therefore any test cases
|
|
@@ -58,6 +59,10 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
58
59
|
parallel_test_suite = NautobotParallelTestSuite
|
|
59
60
|
|
|
60
61
|
exclude_tags = ["integration", "migration_test"]
|
|
62
|
+
if "example_app" not in settings.PLUGINS:
|
|
63
|
+
exclude_tags.append("example_app")
|
|
64
|
+
if "example_app_with_view_override" not in settings.PLUGINS:
|
|
65
|
+
exclude_tags.append("example_app_with_view_override")
|
|
61
66
|
|
|
62
67
|
@classmethod
|
|
63
68
|
def add_arguments(cls, parser):
|
|
@@ -157,6 +162,8 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
157
162
|
db_command = [*command, "--database", alias]
|
|
158
163
|
call_command(*db_command)
|
|
159
164
|
|
|
165
|
+
call_command("post_upgrade")
|
|
166
|
+
|
|
160
167
|
if self.parallel > 1:
|
|
161
168
|
for index in range(self.parallel):
|
|
162
169
|
with time_keeper.timed(f" Cloning '{alias}'"):
|
nautobot/core/tests/test_api.py
CHANGED
|
@@ -533,10 +533,12 @@ class ModelViewSetMixinTest(testing.APITestCase):
|
|
|
533
533
|
self.user.is_superuser = True
|
|
534
534
|
self.user.save()
|
|
535
535
|
|
|
536
|
-
#
|
|
536
|
+
# With exclude_m2m query parameter set to False
|
|
537
537
|
view = self.SimpleIPAddressViewSet()
|
|
538
538
|
view.action_map = {"get": "list"}
|
|
539
|
-
request = APIRequestFactory().get(
|
|
539
|
+
request = APIRequestFactory().get(
|
|
540
|
+
reverse("ipam-api:ipaddress-list"), headers=self.header, data={"exclude_m2m": False}
|
|
541
|
+
)
|
|
540
542
|
force_authenticate(request, user=self.user)
|
|
541
543
|
request = view.initialize_request(request)
|
|
542
544
|
view.setup(request)
|
|
@@ -561,7 +563,7 @@ class ModelViewSetMixinTest(testing.APITestCase):
|
|
|
561
563
|
list(instance.vm_interfaces.all())
|
|
562
564
|
list(instance.tags.all())
|
|
563
565
|
|
|
564
|
-
# With exclude_m2m query parameter
|
|
566
|
+
# With exclude_m2m query parameter set to True
|
|
565
567
|
view = self.SimpleIPAddressViewSet()
|
|
566
568
|
view.action_map = {"get": "list"}
|
|
567
569
|
request = APIRequestFactory().get(
|
|
@@ -35,7 +35,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
35
35
|
item = ViewNameBreadcrumbItem(view_name="home", label="Home")
|
|
36
36
|
context = Context({})
|
|
37
37
|
|
|
38
|
-
url, label = item.as_pair(context)
|
|
38
|
+
url, label = next(item.as_pair(context))
|
|
39
39
|
|
|
40
40
|
self.assertEqual(url, "/")
|
|
41
41
|
self.assertEqual(label, "Home")
|
|
@@ -50,7 +50,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
50
50
|
)
|
|
51
51
|
context = Context({})
|
|
52
52
|
|
|
53
|
-
url, label = item.as_pair(context)
|
|
53
|
+
url, label = next(item.as_pair(context))
|
|
54
54
|
|
|
55
55
|
self.assertEqual(url, f"/dcim/location-types/{self.location_type.pk}/?name=test")
|
|
56
56
|
self.assertEqual(label, "Filtered Locations Types")
|
|
@@ -65,7 +65,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
65
65
|
)
|
|
66
66
|
context = Context({"object": self.location_type})
|
|
67
67
|
|
|
68
|
-
url, label = item.as_pair(context)
|
|
68
|
+
url, label = next(item.as_pair(context))
|
|
69
69
|
|
|
70
70
|
self.assertEqual(
|
|
71
71
|
url, f"/dcim/location-types/{self.location_type.pk}/?{urlencode({'name': self.location_type.name})}"
|
|
@@ -79,7 +79,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
79
79
|
label=lambda context: f"Hi, {context['user']}!",
|
|
80
80
|
)
|
|
81
81
|
context = Context({"user": "Frodo"})
|
|
82
|
-
url, label = item.as_pair(context)
|
|
82
|
+
url, label = next(item.as_pair(context))
|
|
83
83
|
self.assertEqual(url, "/")
|
|
84
84
|
self.assertEqual(label, "Hi, Frodo!")
|
|
85
85
|
|
|
@@ -148,7 +148,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
148
148
|
item = ModelBreadcrumbItem(**test_case["kwargs"])
|
|
149
149
|
context = Context({"object": self.location_type, "model_type": Device, "device_name": "abc"})
|
|
150
150
|
|
|
151
|
-
url, label = item.as_pair(context)
|
|
151
|
+
url, label = next(item.as_pair(context))
|
|
152
152
|
|
|
153
153
|
self.assertEqual(url, test_case["expected_url"])
|
|
154
154
|
self.assertEqual(label, test_case["expected_label"])
|
|
@@ -157,7 +157,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
157
157
|
item = ModelBreadcrumbItem(model_key="object", label="custom LaBeL")
|
|
158
158
|
context = Context({"object": self.location_type})
|
|
159
159
|
|
|
160
|
-
_, label = item.as_pair(context)
|
|
160
|
+
_, label = next(item.as_pair(context))
|
|
161
161
|
self.assertEqual(label, "custom LaBeL")
|
|
162
162
|
|
|
163
163
|
def test_model_item_from_context(self):
|
|
@@ -165,7 +165,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
165
165
|
item = ModelBreadcrumbItem(model_key="object")
|
|
166
166
|
context = Context({"object": self.location_type})
|
|
167
167
|
|
|
168
|
-
url, label = item.as_pair(context)
|
|
168
|
+
url, label = next(item.as_pair(context))
|
|
169
169
|
|
|
170
170
|
self.assertEqual(url, "/dcim/location-types/")
|
|
171
171
|
self.assertEqual(label, "Location Types")
|
|
@@ -175,7 +175,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
175
175
|
item = InstanceBreadcrumbItem(instance_key="object")
|
|
176
176
|
context = Context({"object": self.location_type})
|
|
177
177
|
|
|
178
|
-
url, label = item.as_pair(context)
|
|
178
|
+
url, label = next(item.as_pair(context))
|
|
179
179
|
|
|
180
180
|
self.assertEqual(url, f"/dcim/location-types/{self.location_type.pk}/")
|
|
181
181
|
self.assertEqual(label, str(self.location_type))
|
|
@@ -192,7 +192,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
192
192
|
|
|
193
193
|
for item in items:
|
|
194
194
|
with self.subTest():
|
|
195
|
-
_, label = item.as_pair(context)
|
|
195
|
+
_, label = next(item.as_pair(context))
|
|
196
196
|
self.assertEqual(label, "Custom Label")
|
|
197
197
|
|
|
198
198
|
def test_no_reverse_match(self):
|
|
@@ -200,7 +200,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
200
200
|
item = ViewNameBreadcrumbItem(view_name="nonexistent")
|
|
201
201
|
context = Context({})
|
|
202
202
|
|
|
203
|
-
url, label = item.as_pair(context)
|
|
203
|
+
url, label = next(item.as_pair(context))
|
|
204
204
|
|
|
205
205
|
self.assertEqual(url, "")
|
|
206
206
|
self.assertEqual(label, "")
|
|
@@ -210,12 +210,12 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
210
210
|
context = Context({})
|
|
211
211
|
item = InstanceBreadcrumbItem(instance_key="missing_key")
|
|
212
212
|
|
|
213
|
-
url, label = item.as_pair(context)
|
|
213
|
+
url, label = next(item.as_pair(context))
|
|
214
214
|
self.assertEqual(url, "")
|
|
215
215
|
self.assertEqual(label, "")
|
|
216
216
|
|
|
217
217
|
item = ModelBreadcrumbItem(model_key="missing_key")
|
|
218
|
-
url, label = item.as_pair(context)
|
|
218
|
+
url, label = next(item.as_pair(context))
|
|
219
219
|
self.assertEqual(url, "")
|
|
220
220
|
self.assertEqual(label, "")
|
|
221
221
|
|
|
@@ -223,7 +223,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
223
223
|
context = Context({"object": LocationType.objects.create(name="custom name")})
|
|
224
224
|
item = InstanceBreadcrumbItem()
|
|
225
225
|
|
|
226
|
-
_, label = item.as_pair(context)
|
|
226
|
+
_, label = next(item.as_pair(context))
|
|
227
227
|
self.assertEqual(label, "custom name")
|
|
228
228
|
|
|
229
229
|
def test_instance_item_is_working_with_directly_passed_instance(self):
|
|
@@ -231,7 +231,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
231
231
|
instance = LocationType.objects.create(name="cUsToM CoUnTrY")
|
|
232
232
|
item = InstanceBreadcrumbItem(instance=instance)
|
|
233
233
|
|
|
234
|
-
url, label = item.as_pair(context)
|
|
234
|
+
url, label = next(item.as_pair(context))
|
|
235
235
|
self.assertEqual(url, f"/dcim/location-types/{instance.pk}/")
|
|
236
236
|
self.assertEqual(label, "cUsToM CoUnTrY")
|
|
237
237
|
|
|
@@ -244,17 +244,17 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
244
244
|
context = Context({"object": location, "status": status})
|
|
245
245
|
|
|
246
246
|
item = InstanceBreadcrumbItem(instance=context_object_attr("location_type"))
|
|
247
|
-
url, label = item.as_pair(context)
|
|
247
|
+
url, label = next(item.as_pair(context))
|
|
248
248
|
self.assertEqual(url, f"/dcim/location-types/{location_type.pk}/")
|
|
249
|
-
self.assertEqual(label, "
|
|
249
|
+
self.assertEqual(label, "State")
|
|
250
250
|
|
|
251
251
|
item = BaseBreadcrumbItem(label=context_object_attr("name", context_key="status"))
|
|
252
|
-
url, label = item.as_pair(context)
|
|
252
|
+
url, label = next(item.as_pair(context))
|
|
253
253
|
self.assertEqual(url, "")
|
|
254
254
|
self.assertEqual(label, status.name)
|
|
255
255
|
|
|
256
256
|
item = InstanceBreadcrumbItem(instance=context_object_attr("location_type.parent"))
|
|
257
|
-
url, label = item.as_pair(context)
|
|
257
|
+
url, label = next(item.as_pair(context))
|
|
258
258
|
self.assertEqual(url, f"/dcim/location-types/{parent_location_type.pk}/")
|
|
259
259
|
self.assertEqual(label, "Country")
|
|
260
260
|
|
|
@@ -263,7 +263,7 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
263
263
|
context = Context({"object": LocationType.objects.create(name="custom name", parent=parent_location_type)})
|
|
264
264
|
item = InstanceParentBreadcrumbItem()
|
|
265
265
|
|
|
266
|
-
_, label = item.as_pair(context)
|
|
266
|
+
_, label = next(item.as_pair(context))
|
|
267
267
|
self.assertEqual(label, "cUsToM CoUnTrY")
|
|
268
268
|
|
|
269
269
|
def test_instance_parent_is_create_proper_url(self):
|
|
@@ -273,12 +273,12 @@ class BreadcrumbItemsTestCase(TestCase):
|
|
|
273
273
|
context = Context({"object": location_type})
|
|
274
274
|
|
|
275
275
|
item = InstanceParentBreadcrumbItem()
|
|
276
|
-
url, label = item.as_pair(context)
|
|
276
|
+
url, label = next(item.as_pair(context))
|
|
277
277
|
self.assertEqual(url, f"/dcim/location-types/?parent={parent_location_type.pk}")
|
|
278
278
|
self.assertEqual(label, "Country")
|
|
279
279
|
|
|
280
280
|
item = InstanceParentBreadcrumbItem(parent_query_param="location", parent_lookup_key="name")
|
|
281
|
-
url, label = item.as_pair(context)
|
|
281
|
+
url, label = next(item.as_pair(context))
|
|
282
282
|
self.assertEqual(url, "/dcim/location-types/?location=Country")
|
|
283
283
|
self.assertEqual(label, "Country")
|
|
284
284
|
|
|
@@ -296,8 +296,8 @@ class BreadcrumbsTestCase(TestCase):
|
|
|
296
296
|
breadcrumbs = Breadcrumbs()
|
|
297
297
|
|
|
298
298
|
# Should have defaults for list and details
|
|
299
|
-
self.assertEqual(len(breadcrumbs.items["list"]),
|
|
300
|
-
self.assertEqual(len(breadcrumbs.items["detail"]),
|
|
299
|
+
self.assertEqual(len(breadcrumbs.items["list"]), 0)
|
|
300
|
+
self.assertEqual(len(breadcrumbs.items["detail"]), 2)
|
|
301
301
|
|
|
302
302
|
# Verify adding items
|
|
303
303
|
new_item = BaseBreadcrumbItem()
|
|
@@ -306,7 +306,7 @@ class BreadcrumbsTestCase(TestCase):
|
|
|
306
306
|
self.assertEqual(len(breadcrumbs.items["list"]), 1)
|
|
307
307
|
self.assertEqual(breadcrumbs.items["list"][0], new_item)
|
|
308
308
|
|
|
309
|
-
self.assertEqual(len(breadcrumbs.items["detail"]),
|
|
309
|
+
self.assertEqual(len(breadcrumbs.items["detail"]), 1)
|
|
310
310
|
self.assertEqual(breadcrumbs.items["detail"][0], new_item)
|
|
311
311
|
|
|
312
312
|
self.assertEqual(len(breadcrumbs.items["custom_action"]), 1)
|
|
@@ -325,7 +325,7 @@ class BreadcrumbsTestCase(TestCase):
|
|
|
325
325
|
|
|
326
326
|
# Other defaults should still exist
|
|
327
327
|
self.assertIn("detail", breadcrumbs.items)
|
|
328
|
-
self.assertEqual(len(breadcrumbs.items["detail"]),
|
|
328
|
+
self.assertEqual(len(breadcrumbs.items["detail"]), 2)
|
|
329
329
|
|
|
330
330
|
def test_get_items_from_action_static_method(self):
|
|
331
331
|
"""Test the _get_items_from_action static method."""
|
|
@@ -355,7 +355,6 @@ class BreadcrumbsTestCase(TestCase):
|
|
|
355
355
|
breadcrumbs = Breadcrumbs()
|
|
356
356
|
expected_items = [
|
|
357
357
|
("/dcim/location-types/", "Location Types"),
|
|
358
|
-
(f"/dcim/location-types/{self.location_type.pk}/", str(self.location_type)),
|
|
359
358
|
]
|
|
360
359
|
|
|
361
360
|
# Test with an action that doesn't exist but detail=True
|
|
@@ -366,13 +365,13 @@ class BreadcrumbsTestCase(TestCase):
|
|
|
366
365
|
items = breadcrumbs.get_breadcrumbs_items(context)
|
|
367
366
|
|
|
368
367
|
# Should get 2 items from detail fallback
|
|
369
|
-
self.assertEqual(len(items),
|
|
368
|
+
self.assertEqual(len(items), 1)
|
|
370
369
|
self.assertEqual(items, expected_items)
|
|
371
370
|
|
|
372
371
|
def test_render_method(self):
|
|
373
372
|
"""Test the render method."""
|
|
374
373
|
breadcrumbs = Breadcrumbs()
|
|
375
|
-
context = Context({"
|
|
374
|
+
context = Context({"detail": True, "model": Device})
|
|
376
375
|
|
|
377
376
|
html = breadcrumbs.render(context)
|
|
378
377
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.test import override_settings, TestCase
|
|
2
2
|
|
|
3
3
|
from nautobot.core import checks
|
|
4
|
+
from nautobot.dcim.choices import DeviceUniquenessChoices
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class CheckCoreSettingsTest(TestCase):
|
|
@@ -42,3 +43,30 @@ class CheckCoreSettingsTest(TestCase):
|
|
|
42
43
|
def test_check_maintenance_mode(self):
|
|
43
44
|
"""Error if MAINTENANCE_MODE is set and yet SESSION_ENGINE is still storing sessions in the db."""
|
|
44
45
|
self.assertEqual(checks.check_maintenance_mode(None), [checks.E005])
|
|
46
|
+
|
|
47
|
+
@override_settings(
|
|
48
|
+
DEVICE_NAME_AS_NATURAL_KEY=True,
|
|
49
|
+
)
|
|
50
|
+
def test_check_deprecated_device_name_as_natural_key(self):
|
|
51
|
+
"""Warn if DEVICE_NAME_AS_NATURAL_KEY is defined in settings."""
|
|
52
|
+
self.assertEqual(
|
|
53
|
+
checks.check_deprecated_device_name_as_natural_key(None),
|
|
54
|
+
[checks.W006],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@override_settings(
|
|
58
|
+
DEVICE_UNIQUENESS="invalid_value",
|
|
59
|
+
)
|
|
60
|
+
def test_check_invalid_device_uniqueness_value(self):
|
|
61
|
+
"""Warn if DEVICE_UNIQUENESS is set to an invalid value."""
|
|
62
|
+
self.assertEqual(
|
|
63
|
+
checks.check_valid_value_for_device_uniqueness(None),
|
|
64
|
+
[checks.W007],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@override_settings(
|
|
68
|
+
DEVICE_UNIQUENESS=DeviceUniquenessChoices.NAME,
|
|
69
|
+
)
|
|
70
|
+
def test_check_valid_device_uniqueness_value(self):
|
|
71
|
+
"""No warning if DEVICE_UNIQUENESS is set to a valid value."""
|
|
72
|
+
self.assertEqual(checks.check_valid_value_for_device_uniqueness(None), [])
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from nautobot.core.cli import migrate_deprecated_templates
|
|
2
|
+
from nautobot.core.testing import TestCase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestMigrateTemplates(TestCase):
|
|
6
|
+
def test_template_replacements(self):
|
|
7
|
+
"""Verify that all old templates are replaced by a single new template."""
|
|
8
|
+
audit_dict = {}
|
|
9
|
+
for new_template, old_templates in migrate_deprecated_templates.TEMPLATE_REPLACEMENTS.items():
|
|
10
|
+
for old_template in old_templates:
|
|
11
|
+
self.assertNotIn(old_template, audit_dict)
|
|
12
|
+
audit_dict[old_template] = new_template
|
|
13
|
+
|
|
14
|
+
def test_replace_template_references_no_change(self):
|
|
15
|
+
content = """
|
|
16
|
+
{% extends "base.html" %}
|
|
17
|
+
{% block content %}
|
|
18
|
+
<h1>Hello, World!</h1>
|
|
19
|
+
{% endblock %}
|
|
20
|
+
"""
|
|
21
|
+
replaced_content, was_updated = migrate_deprecated_templates.replace_template_references(content)
|
|
22
|
+
self.assertFalse(was_updated)
|
|
23
|
+
self.assertEqual(replaced_content, content)
|
|
24
|
+
|
|
25
|
+
def test_replace_template_references(self):
|
|
26
|
+
original_content = """
|
|
27
|
+
{% extends "generic/object_bulk_import.html" %}
|
|
28
|
+
{% block content %}
|
|
29
|
+
<h1>Hello, World!</h1>
|
|
30
|
+
{% endblock %}
|
|
31
|
+
"""
|
|
32
|
+
new_content = """
|
|
33
|
+
{% extends "generic/object_bulk_create.html" %}
|
|
34
|
+
{% block content %}
|
|
35
|
+
<h1>Hello, World!</h1>
|
|
36
|
+
{% endblock %}
|
|
37
|
+
"""
|
|
38
|
+
replaced_content, was_updated = migrate_deprecated_templates.replace_template_references(original_content)
|
|
39
|
+
self.assertTrue(was_updated)
|
|
40
|
+
self.assertEqual(replaced_content, new_content)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Test cases for nautobot.core.config module."""
|
|
2
2
|
|
|
3
3
|
from constance.test import override_config
|
|
4
|
-
from django.test import override_settings, TestCase
|
|
4
|
+
from django.test import override_settings, tag, TestCase
|
|
5
5
|
|
|
6
6
|
from nautobot.apps import config as app_config
|
|
7
7
|
from nautobot.core.utils import config
|
|
@@ -37,6 +37,7 @@ class GetSettingsOrConfigTestCase(TestCase):
|
|
|
37
37
|
self.assertRaises(AttributeError, config.get_settings_or_config, "FAKE_SETTING")
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
@tag("example_app")
|
|
40
41
|
class GetAppSettingsOrConfigTestCase(TestCase):
|
|
41
42
|
"""Test the get_app_settings_or_config() helper function."""
|
|
42
43
|
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
1
3
|
from unittest import mock
|
|
2
4
|
|
|
3
5
|
from django import forms as django_forms
|
|
6
|
+
from django.apps import apps as django_apps
|
|
4
7
|
from django.contrib.contenttypes.models import ContentType
|
|
5
8
|
from django.http import QueryDict
|
|
6
|
-
from django.test import
|
|
9
|
+
from django.test import tag
|
|
7
10
|
from django.urls import reverse
|
|
11
|
+
from django_filters.filterset import FilterSet
|
|
8
12
|
from netaddr import IPNetwork
|
|
9
13
|
|
|
10
14
|
from nautobot.core import filters, forms, testing
|
|
11
15
|
from nautobot.core.utils import requests
|
|
16
|
+
from nautobot.core.utils.filtering import get_filterset_parameter_form_field
|
|
12
17
|
from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models
|
|
13
18
|
from nautobot.dcim.tests import test_views
|
|
14
19
|
from nautobot.extras import filters as extras_filters, models as extras_models
|
|
15
20
|
from nautobot.ipam import forms as ipam_forms, models as ipam_models
|
|
16
21
|
|
|
17
22
|
|
|
18
|
-
class ExpandIPAddress(TestCase):
|
|
23
|
+
class ExpandIPAddress(testing.TestCase):
|
|
19
24
|
"""
|
|
20
25
|
Validate the operation of expand_ipaddress_pattern().
|
|
21
26
|
"""
|
|
@@ -198,7 +203,7 @@ class ExpandIPAddress(TestCase):
|
|
|
198
203
|
sorted(forms.expand_ipaddress_pattern("1.2.3.[4,,5]/32", 4))
|
|
199
204
|
|
|
200
205
|
|
|
201
|
-
class ExpandAlphanumeric(TestCase):
|
|
206
|
+
class ExpandAlphanumeric(testing.TestCase):
|
|
202
207
|
"""
|
|
203
208
|
Validate the operation of expand_alphanumeric_pattern().
|
|
204
209
|
"""
|
|
@@ -332,7 +337,7 @@ class ExpandAlphanumeric(TestCase):
|
|
|
332
337
|
sorted(forms.expand_alphanumeric_pattern("r[a,,b]a"))
|
|
333
338
|
|
|
334
339
|
|
|
335
|
-
class AddFieldToFormClassTest(TestCase):
|
|
340
|
+
class AddFieldToFormClassTest(testing.TestCase):
|
|
336
341
|
def test_field_added(self):
|
|
337
342
|
"""
|
|
338
343
|
Test adding of a new field to an existing form.
|
|
@@ -361,7 +366,7 @@ class AddFieldToFormClassTest(TestCase):
|
|
|
361
366
|
)
|
|
362
367
|
|
|
363
368
|
|
|
364
|
-
class DynamicModelChoiceFieldTest(TestCase):
|
|
369
|
+
class DynamicModelChoiceFieldTest(testing.TestCase):
|
|
365
370
|
"""Tests for DynamicModelChoiceField."""
|
|
366
371
|
|
|
367
372
|
def setUp(self):
|
|
@@ -397,7 +402,7 @@ class DynamicModelChoiceFieldTest(TestCase):
|
|
|
397
402
|
self.assertEqual(self.field_with_to_field_name.prepare_value(address), address.address)
|
|
398
403
|
|
|
399
404
|
|
|
400
|
-
class DynamicModelMultipleChoiceFieldTest(TestCase):
|
|
405
|
+
class DynamicModelMultipleChoiceFieldTest(testing.TestCase):
|
|
401
406
|
"""Tests for DynamicModelMultipleChoiceField."""
|
|
402
407
|
|
|
403
408
|
def setUp(self):
|
|
@@ -434,7 +439,7 @@ class DynamicModelMultipleChoiceFieldTest(TestCase):
|
|
|
434
439
|
)
|
|
435
440
|
|
|
436
441
|
|
|
437
|
-
class MultiValueCharFieldTest(TestCase):
|
|
442
|
+
class MultiValueCharFieldTest(testing.TestCase):
|
|
438
443
|
def setUp(self):
|
|
439
444
|
self.filter = filters.MultiValueCharFilter()
|
|
440
445
|
self.field = forms.MultiValueCharField()
|
|
@@ -467,7 +472,7 @@ class MultiValueCharFieldTest(TestCase):
|
|
|
467
472
|
)
|
|
468
473
|
|
|
469
474
|
|
|
470
|
-
class NumericArrayFieldTest(TestCase):
|
|
475
|
+
class NumericArrayFieldTest(testing.TestCase):
|
|
471
476
|
def setUp(self):
|
|
472
477
|
super().setUp()
|
|
473
478
|
# We need to use a field with required=False so we can test empty/None inputs
|
|
@@ -497,7 +502,7 @@ class NumericArrayFieldTest(TestCase):
|
|
|
497
502
|
self.field.clean(test)
|
|
498
503
|
|
|
499
504
|
|
|
500
|
-
class AddressFieldMixinTest(TestCase):
|
|
505
|
+
class AddressFieldMixinTest(testing.TestCase):
|
|
501
506
|
"""Test cases for the AddressFieldMixin."""
|
|
502
507
|
|
|
503
508
|
def setUp(self):
|
|
@@ -527,7 +532,7 @@ class AddressFieldMixinTest(TestCase):
|
|
|
527
532
|
mock_init.assert_called_with(initial=self.initial, instance=self.ip)
|
|
528
533
|
|
|
529
534
|
|
|
530
|
-
class PrefixFieldMixinTest(TestCase):
|
|
535
|
+
class PrefixFieldMixinTest(testing.TestCase):
|
|
531
536
|
"""Test cases for the PrefixFieldMixin."""
|
|
532
537
|
|
|
533
538
|
def setUp(self):
|
|
@@ -576,7 +581,7 @@ class JSONFieldTest(testing.TestCase):
|
|
|
576
581
|
self.assertEqual('"I am UTF-8! 😀"', forms.JSONField().prepare_value("I am UTF-8! 😀"))
|
|
577
582
|
|
|
578
583
|
|
|
579
|
-
class MultiMatchModelMultipleChoiceFieldTest(TestCase):
|
|
584
|
+
class MultiMatchModelMultipleChoiceFieldTest(testing.TestCase):
|
|
580
585
|
def test_clean(self):
|
|
581
586
|
field = forms.MultiMatchModelMultipleChoiceField(
|
|
582
587
|
queryset=ipam_models.VLANGroup.objects.all(), to_field_name="name"
|
|
@@ -604,20 +609,57 @@ class MultiMatchModelMultipleChoiceFieldTest(TestCase):
|
|
|
604
609
|
field.clean(value)
|
|
605
610
|
|
|
606
611
|
|
|
607
|
-
class WidgetsTest(TestCase):
|
|
612
|
+
class WidgetsTest(testing.TestCase):
|
|
608
613
|
def test_api_select_add_query_param_with_utf8(self):
|
|
609
614
|
widget = forms.APISelect()
|
|
610
615
|
widget.add_query_param("utf8", "I am UTF-8! 😀")
|
|
611
616
|
self.assertEqual('["I am UTF-8! 😀"]', widget.attrs["data-query-param-utf8"])
|
|
612
617
|
|
|
613
618
|
|
|
614
|
-
class DynamicFilterFormTest(TestCase):
|
|
619
|
+
class DynamicFilterFormTest(testing.TestCase):
|
|
620
|
+
def test_get_filterset_parameter_form_field_all_filters(self):
|
|
621
|
+
"""
|
|
622
|
+
Test every FilterSet to validate that Plural names are correctly mapped in get_filterset_parameter_form_field.
|
|
623
|
+
"""
|
|
624
|
+
filterset_classes = set()
|
|
625
|
+
for app_config in django_apps.get_app_configs():
|
|
626
|
+
try:
|
|
627
|
+
filters_mod = sys.modules.get(f"{app_config.name}.filters")
|
|
628
|
+
if not filters_mod:
|
|
629
|
+
continue
|
|
630
|
+
for _name, obj in inspect.getmembers(filters_mod):
|
|
631
|
+
if (
|
|
632
|
+
inspect.isclass(obj) # Check if obj is a class
|
|
633
|
+
and issubclass(obj, FilterSet) # Check if obj is a subclass of FilterSet
|
|
634
|
+
and obj is not FilterSet # Exclude the base FilterSet class itself
|
|
635
|
+
and getattr(getattr(obj, "_meta", None), "model", None)
|
|
636
|
+
is not None # Ensure the FilterSet has a model defined
|
|
637
|
+
):
|
|
638
|
+
filterset_classes.add(obj)
|
|
639
|
+
except Exception as e:
|
|
640
|
+
# This test might start failing if an app's filters.py gets a design change.
|
|
641
|
+
self.fail(f"Error processing app '{app_config.name}': {e}")
|
|
642
|
+
for filterset_class in filterset_classes:
|
|
643
|
+
filterset = filterset_class()
|
|
644
|
+
model = filterset._meta.model
|
|
645
|
+
for filter_name in filterset.filters.keys():
|
|
646
|
+
try:
|
|
647
|
+
field = get_filterset_parameter_form_field(model, filter_name, filterset=filterset)
|
|
648
|
+
self.assertIsNotNone(field, "Field was unexpectedly None")
|
|
649
|
+
except KeyError as e:
|
|
650
|
+
self.fail(
|
|
651
|
+
f"A filter failed to operate due to mismatched plural name:"
|
|
652
|
+
f" Check MODEL_VERBOSE_NAME_PLURAL_TO_FEATURE_NAME_MAPPING:"
|
|
653
|
+
f" FilterClass: {filterset_class.__name__} name: {filter_name}: {e}"
|
|
654
|
+
)
|
|
655
|
+
|
|
615
656
|
# TODO(timizuo): investigate why test fails on CI
|
|
616
657
|
# def test_dynamic_filter_form_with_missing_attr(self):
|
|
617
658
|
# with self.assertRaises(AttributeError) as err:
|
|
618
659
|
# DynamicFilterForm()
|
|
619
660
|
# self.assertEqual("'DynamicFilterForm' object requires `filterset_class` attribute", str(err.exception))
|
|
620
661
|
|
|
662
|
+
@tag("example_app")
|
|
621
663
|
def test_dynamic_filter_form(self):
|
|
622
664
|
form = forms.DynamicFilterForm(filterset=extras_filters.StatusFilterSet())
|
|
623
665
|
location_form = forms.DynamicFilterForm(filterset=dcim_filters.LocationFilterSet())
|