nautobot 2.0.0a2__py3-none-any.whl → 2.0.0b1__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/__init__.py +1 -5
- nautobot/apps/api.py +6 -8
- nautobot/apps/forms.py +0 -2
- nautobot/apps/ui.py +0 -8
- nautobot/circuits/api/serializers.py +9 -119
- nautobot/circuits/api/urls.py +1 -1
- nautobot/circuits/api/views.py +0 -1
- nautobot/circuits/choices.py +0 -2
- nautobot/circuits/filters.py +7 -6
- nautobot/circuits/forms.py +3 -73
- nautobot/circuits/migrations/0001_initial_part_1.py +0 -1
- nautobot/circuits/migrations/0002_initial_part_2.py +0 -1
- nautobot/circuits/migrations/0003_auto_slug.py +0 -1
- nautobot/circuits/migrations/0004_increase_provider_account_length.py +0 -1
- nautobot/circuits/migrations/0005_providernetwork.py +0 -1
- nautobot/circuits/migrations/0006_cache_circuit_terminations.py +0 -1
- nautobot/circuits/migrations/0007_circuitterminations_primary_model.py +0 -1
- nautobot/circuits/migrations/0008_add_natural_indexing.py +0 -1
- nautobot/circuits/migrations/0009_circuittermination_location.py +0 -1
- nautobot/circuits/migrations/0010_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/circuits/migrations/0011_remove_site_foreign_key_from_circuit_termination_class.py +0 -1
- nautobot/circuits/migrations/0012_created_datetime.py +0 -1
- nautobot/circuits/migrations/0013_alter_circuittermination__path.py +0 -1
- nautobot/circuits/migrations/0014_related_name_changes.py +1 -2
- nautobot/circuits/migrations/0015_remove_circuittype_provider_slug.py +20 -0
- nautobot/circuits/migrations/0016_tagsfield.py +34 -0
- nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
- nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
- nautobot/circuits/models.py +3 -93
- nautobot/circuits/navigation.py +14 -69
- nautobot/circuits/signals.py +0 -2
- nautobot/circuits/tables.py +42 -5
- nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/provider_create.html +0 -1
- nautobot/circuits/templates/circuits/provider_retrieve.html +1 -1
- nautobot/circuits/tests/integration/test_relationships.py +13 -16
- nautobot/circuits/tests/test_api.py +13 -43
- nautobot/circuits/tests/test_filters.py +20 -15
- nautobot/circuits/tests/test_models.py +7 -3
- nautobot/circuits/tests/test_views.py +57 -67
- nautobot/circuits/views.py +18 -9
- nautobot/core/api/__init__.py +8 -2
- nautobot/core/api/authentication.py +0 -3
- nautobot/core/api/fields.py +15 -6
- nautobot/core/api/filter_backends.py +3 -2
- nautobot/core/api/metadata.py +237 -30
- nautobot/core/api/mixins.py +94 -0
- nautobot/core/api/pagination.py +3 -3
- nautobot/core/api/parsers.py +154 -0
- nautobot/core/api/renderers.py +153 -2
- nautobot/core/api/schema.py +47 -3
- nautobot/core/api/serializers.py +377 -37
- nautobot/core/api/urls.py +11 -3
- nautobot/core/api/utils.py +174 -2
- nautobot/core/api/versioning.py +32 -10
- nautobot/core/api/views.py +266 -75
- nautobot/core/apps/__init__.py +138 -221
- nautobot/core/celery/__init__.py +112 -41
- nautobot/core/celery/backends.py +19 -13
- nautobot/core/celery/control.py +46 -0
- nautobot/core/celery/encoders.py +53 -0
- nautobot/core/celery/log.py +38 -0
- nautobot/core/celery/schedulers.py +23 -4
- nautobot/core/celery/task.py +1 -16
- nautobot/core/checks.py +0 -27
- nautobot/core/choices.py +21 -113
- nautobot/core/{cli.py → cli/__init__.py} +1 -2
- nautobot/core/cli/__main__.py +3 -0
- nautobot/core/constants.py +25 -43
- nautobot/core/context_processors.py +12 -0
- nautobot/core/filters.py +2 -2
- nautobot/core/forms/__init__.py +0 -4
- nautobot/core/forms/fields.py +39 -68
- nautobot/core/forms/forms.py +27 -27
- nautobot/core/forms/utils.py +7 -59
- nautobot/core/forms/widgets.py +0 -1
- nautobot/core/graphql/__init__.py +2 -2
- nautobot/core/graphql/schema.py +4 -27
- nautobot/core/jobs/__init__.py +75 -0
- nautobot/core/management/commands/build_ui.py +255 -0
- nautobot/core/management/commands/celery.py +0 -1
- nautobot/core/management/commands/generate_test_data.py +18 -13
- nautobot/core/management/commands/post_upgrade.py +24 -24
- nautobot/core/management/commands/validate_models.py +0 -1
- nautobot/core/middleware.py +0 -1
- nautobot/core/models/__init__.py +26 -1
- nautobot/core/models/fields.py +24 -5
- nautobot/core/models/generics.py +2 -46
- nautobot/core/models/managers.py +5 -0
- nautobot/core/models/name_color_content_types.py +1 -19
- nautobot/core/models/tree_queries.py +14 -4
- nautobot/core/models/utils.py +9 -10
- nautobot/core/models/validators.py +17 -8
- nautobot/core/releases.py +8 -10
- nautobot/core/settings.py +81 -53
- nautobot/core/tables.py +5 -5
- nautobot/core/tasks.py +4 -7
- nautobot/core/templates/base.html +1 -49
- nautobot/core/templates/base_django.html +49 -0
- nautobot/core/templates/base_react.html +55 -0
- nautobot/core/templates/buttons/export.html +6 -4
- nautobot/core/templates/generic/object_bulk_create.html +10 -21
- nautobot/core/templates/generic/object_list.html +4 -1
- nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
- nautobot/core/templates/inc/footer.html +1 -0
- nautobot/core/templates/inc/javascript.html +0 -14
- nautobot/core/templates/inc/nav_menu.html +28 -33
- nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
- nautobot/core/templates/inc/relationships_table_rows.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +8 -25
- nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
- nautobot/core/templates/rest_framework/api.html +8 -0
- nautobot/core/templatetags/buttons.py +32 -29
- nautobot/core/templatetags/helpers.py +1 -1
- nautobot/core/testing/__init__.py +47 -44
- nautobot/core/testing/api.py +365 -47
- nautobot/core/testing/filters.py +12 -7
- nautobot/core/testing/integration.py +1 -1
- nautobot/core/testing/migrations.py +2 -0
- nautobot/core/testing/mixins.py +22 -12
- nautobot/core/testing/schema.py +2 -1
- nautobot/core/testing/views.py +28 -51
- nautobot/core/tests/integration/test_filters.py +17 -8
- nautobot/core/tests/integration/test_navbar.py +11 -34
- nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
- nautobot/core/tests/nautobot_config.py +2 -3
- nautobot/core/tests/runner.py +0 -1
- nautobot/core/tests/test_api.py +290 -24
- nautobot/core/tests/test_authentication.py +57 -14
- nautobot/core/tests/test_checks.py +0 -7
- nautobot/core/tests/test_choices.py +0 -1
- nautobot/core/tests/test_filters.py +117 -110
- nautobot/core/tests/test_forms.py +47 -110
- nautobot/core/tests/test_graphql.py +158 -135
- nautobot/core/tests/test_logging.py +4 -1
- nautobot/core/tests/test_managers.py +3 -5
- nautobot/core/tests/test_models.py +2 -0
- nautobot/core/tests/test_ordering.py +0 -2
- nautobot/core/tests/test_paginator.py +3 -1
- nautobot/core/tests/test_releases.py +12 -12
- nautobot/core/tests/test_templatetags_helpers.py +7 -4
- nautobot/core/tests/test_utils.py +112 -78
- nautobot/core/tests/test_views.py +12 -17
- nautobot/core/tests/test_views_utils.py +6 -9
- nautobot/core/utils/data.py +17 -0
- nautobot/core/utils/deprecation.py +13 -20
- nautobot/core/utils/filtering.py +53 -9
- nautobot/core/utils/git.py +12 -4
- nautobot/core/utils/lookup.py +3 -1
- nautobot/core/utils/requests.py +23 -116
- nautobot/core/views/__init__.py +1 -2
- nautobot/core/views/generic.py +131 -119
- nautobot/core/views/mixins.py +53 -62
- nautobot/core/views/paginator.py +0 -1
- nautobot/core/views/renderers.py +14 -12
- nautobot/core/views/utils.py +87 -4
- nautobot/dcim/api/serializers.py +160 -672
- nautobot/dcim/api/urls.py +1 -1
- nautobot/dcim/api/views.py +7 -46
- nautobot/dcim/choices.py +2 -25
- nautobot/dcim/elevations.py +0 -1
- nautobot/dcim/factory.py +15 -4
- nautobot/dcim/filters/__init__.py +42 -13
- nautobot/dcim/form_mixins.py +1 -27
- nautobot/dcim/forms.py +58 -797
- nautobot/dcim/management/commands/trace_paths.py +0 -1
- nautobot/dcim/migrations/0001_initial_part_1.py +0 -1
- nautobot/dcim/migrations/0002_initial_part_2.py +0 -1
- nautobot/dcim/migrations/0003_initial_part_3.py +0 -1
- nautobot/dcim/migrations/0004_initial_part_4.py +0 -1
- nautobot/dcim/migrations/0005_device_local_context_schema.py +0 -1
- nautobot/dcim/migrations/0006_auto_slug.py +0 -1
- nautobot/dcim/migrations/0007_device_secrets_group.py +0 -1
- nautobot/dcim/migrations/0008_increase_all_serial_lengths.py +0 -1
- nautobot/dcim/migrations/0009_add_natural_indexing.py +0 -1
- nautobot/dcim/migrations/0010_interface_status.py +0 -1
- nautobot/dcim/migrations/0011_interface_status_data_migration.py +0 -1
- nautobot/dcim/migrations/0012_interface_parent_bridge.py +0 -1
- nautobot/dcim/migrations/0013_location_location_type.py +0 -1
- nautobot/dcim/migrations/0014_location_status_data_migration.py +0 -1
- nautobot/dcim/migrations/0015_device_components__changeloggedmodel.py +0 -1
- nautobot/dcim/migrations/0016_device_components__timestamp_data_migration.py +0 -1
- nautobot/dcim/migrations/0017_locationtype_nestable.py +0 -1
- nautobot/dcim/migrations/0018_device_redundancy_group.py +0 -1
- nautobot/dcim/migrations/0019_device_redundancy_group_data_migration.py +0 -1
- nautobot/dcim/migrations/0020_move_site_fields_to_location_model.py +0 -1
- nautobot/dcim/migrations/0021_mptt_to_tree_queries.py +0 -1
- nautobot/dcim/migrations/0022_interface_mac_address_data_migration.py +0 -1
- nautobot/dcim/migrations/0023_alter_interface_mac_address.py +0 -1
- nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -2
- nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -14
- nautobot/dcim/migrations/0026_rename_device_and_rack_role.py +0 -1
- nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -2
- nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -2
- nautobot/dcim/migrations/0029_add_tree_managers_and_foreign_keys_pre_data_migration.py +0 -1
- nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
- nautobot/dcim/migrations/0031_rename_path_end_point_related_name.py +0 -1
- nautobot/dcim/migrations/0032_remove_site_foreign_key_from_dcim_models.py +0 -1
- nautobot/dcim/migrations/0033_created_datetime.py +0 -1
- nautobot/dcim/migrations/0034_fixup_fks_and_related_names.py +0 -1
- nautobot/dcim/migrations/0035_related_name_changes.py +1 -2
- nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -2
- nautobot/dcim/migrations/0037_interface_ip_addresses_m2m.py +0 -1
- nautobot/dcim/migrations/0038_alter_location_managers.py +0 -1
- nautobot/dcim/migrations/0039_remove_slug.py +24 -0
- nautobot/dcim/migrations/0040_tagsfield.py +109 -0
- nautobot/dcim/migrations/0041_ipam__namespaces.py +25 -0
- nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
- nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
- nautobot/dcim/models/cables.py +4 -35
- nautobot/dcim/models/device_component_templates.py +7 -2
- nautobot/dcim/models/device_components.py +26 -203
- nautobot/dcim/models/devices.py +30 -152
- nautobot/dcim/models/locations.py +3 -64
- nautobot/dcim/models/power.py +3 -51
- nautobot/dcim/models/racks.py +7 -86
- nautobot/dcim/navigation.py +141 -467
- nautobot/dcim/signals.py +0 -2
- nautobot/dcim/tables/devices.py +8 -5
- nautobot/dcim/tables/devicetypes.py +1 -1
- nautobot/dcim/tables/locations.py +2 -2
- nautobot/dcim/tables/power.py +2 -2
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/device.html +15 -4
- nautobot/dcim/templates/dcim/device_edit.html +6 -0
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +0 -1
- nautobot/dcim/templates/dcim/devicetype.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +4 -0
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/location.html +16 -1
- nautobot/dcim/templates/dcim/locationtype.html +15 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/rackgroup.html +0 -12
- nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
- nautobot/dcim/tests/test_api.py +202 -130
- nautobot/dcim/tests/test_cablepaths.py +47 -42
- nautobot/dcim/tests/test_filters.py +156 -134
- nautobot/dcim/tests/test_forms.py +12 -213
- nautobot/dcim/tests/test_graphql.py +8 -3
- nautobot/dcim/tests/test_migrations.py +6 -11
- nautobot/dcim/tests/test_models.py +208 -158
- nautobot/dcim/tests/test_natural_ordering.py +12 -14
- nautobot/dcim/tests/test_signals.py +7 -4
- nautobot/dcim/tests/test_views.py +270 -264
- nautobot/dcim/urls.py +21 -26
- nautobot/dcim/views.py +14 -156
- nautobot/docs/additional-features/caching.md +6 -87
- nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
- nautobot/docs/additional-features/jobs.md +179 -197
- nautobot/docs/administration/nautobot-server.md +9 -24
- nautobot/docs/administration/nautobot-shell.md +6 -6
- nautobot/docs/administration/replicating-nautobot.md +0 -10
- nautobot/docs/configuration/index.md +9 -9
- nautobot/docs/configuration/optional-settings.md +32 -61
- nautobot/docs/configuration/required-settings.md +11 -52
- nautobot/docs/development/application-registry.md +2 -13
- nautobot/docs/development/best-practices.md +2 -1
- nautobot/docs/development/docker-compose-advanced-use-cases.md +1 -1
- nautobot/docs/development/extending-models.md +15 -17
- nautobot/docs/development/generic-views.md +0 -2
- nautobot/docs/development/getting-started.md +56 -6
- nautobot/docs/development/navigation-menu.md +22 -93
- nautobot/docs/development/react-ui.md +105 -0
- nautobot/docs/development/release-checklist.md +3 -3
- nautobot/docs/development/role-internals.md +1 -3
- nautobot/docs/development/style-guide.md +6 -4
- nautobot/docs/development/templates.md +2 -1
- nautobot/docs/docker/index.md +16 -14
- nautobot/docs/index.md +7 -3
- nautobot/docs/installation/index.md +4 -1
- nautobot/docs/installation/migrating-from-netbox.md +12 -43
- nautobot/docs/installation/migrating-from-postgresql.md +1 -1
- nautobot/docs/installation/nautobot.md +1 -1
- nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/docs/installation/upgrading-from-nautobot-v1.md +190 -636
- nautobot/docs/installation/upgrading.md +5 -2
- nautobot/docs/models/dcim/device.md +3 -0
- nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
- nautobot/docs/models/extras/computedfield.md +4 -4
- nautobot/docs/models/extras/dynamicgroup.md +9 -9
- nautobot/docs/models/extras/gitrepository.md +3 -0
- nautobot/docs/models/extras/job.md +1 -0
- nautobot/docs/models/extras/jobbutton.md +18 -13
- nautobot/docs/models/extras/jobhook.md +7 -4
- nautobot/docs/models/extras/jobresult.md +6 -2
- nautobot/docs/models/extras/relationship.md +2 -2
- nautobot/docs/models/extras/status.md +6 -19
- nautobot/docs/models/ipam/ipaddress.md +3 -0
- nautobot/docs/models/ipam/vrf.md +0 -3
- nautobot/docs/models/virtualization/virtualmachine.md +3 -0
- nautobot/docs/plugins/development.md +92 -24
- nautobot/docs/release-notes/version-1.5.md +96 -0
- nautobot/docs/release-notes/version-2.0.md +216 -0
- nautobot/docs/requirements.txt +5 -4
- nautobot/docs/rest-api/overview.md +384 -215
- nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
- nautobot/extras/admin.py +3 -5
- nautobot/extras/api/customfields.py +15 -39
- nautobot/extras/api/fields.py +0 -11
- nautobot/extras/api/mixins.py +45 -0
- nautobot/extras/api/relationships.py +63 -159
- nautobot/extras/api/serializers.py +165 -706
- nautobot/extras/api/urls.py +1 -1
- nautobot/extras/api/views.py +295 -282
- nautobot/extras/apps.py +4 -7
- nautobot/extras/choices.py +11 -22
- nautobot/extras/constants.py +9 -3
- nautobot/extras/datasources/__init__.py +2 -0
- nautobot/extras/datasources/git.py +135 -186
- nautobot/extras/datasources/registry.py +25 -35
- nautobot/extras/factory.py +1 -3
- nautobot/extras/filters/__init__.py +49 -47
- nautobot/extras/filters/mixins.py +10 -8
- nautobot/extras/forms/forms.py +72 -148
- nautobot/extras/forms/mixins.py +34 -57
- nautobot/extras/health_checks.py +0 -33
- nautobot/extras/jobs.py +387 -566
- nautobot/extras/management/__init__.py +55 -48
- nautobot/extras/management/commands/renaturalize.py +0 -1
- nautobot/extras/management/commands/runjob.py +24 -62
- nautobot/extras/management/commands/webhook_receiver.py +0 -1
- nautobot/extras/managers.py +30 -7
- nautobot/extras/migrations/0001_initial_part_1.py +0 -1
- nautobot/extras/migrations/0002_initial_part_2.py +0 -1
- nautobot/extras/migrations/0003_initial_part_3.py +0 -1
- nautobot/extras/migrations/0004_populate_default_status_records.py +0 -1
- nautobot/extras/migrations/0005_configcontext_device_types.py +0 -1
- nautobot/extras/migrations/0006_graphqlquery.py +0 -1
- nautobot/extras/migrations/0007_configcontextschema.py +0 -1
- nautobot/extras/migrations/0008_jobresult__custom_field_data.py +0 -1
- nautobot/extras/migrations/0009_computedfield.py +0 -1
- nautobot/extras/migrations/0010_change_cf_validation_max_min_field_to_bigint.py +0 -1
- nautobot/extras/migrations/0011_fileattachment_fileproxy.py +0 -1
- nautobot/extras/migrations/0012_healthchecktestmodel.py +0 -1
- nautobot/extras/migrations/0013_default_fallback_value_computedfield.py +0 -1
- nautobot/extras/migrations/0014_auto_slug.py +0 -1
- nautobot/extras/migrations/0015_scheduled_job.py +0 -1
- nautobot/extras/migrations/0016_secret.py +0 -1
- nautobot/extras/migrations/0017_joblogentry.py +0 -1
- nautobot/extras/migrations/0018_joblog_data_migration.py +0 -2
- nautobot/extras/migrations/0019_joblogentry__meta_options__related_name.py +0 -1
- nautobot/extras/migrations/0020_customfield_changelog.py +0 -1
- nautobot/extras/migrations/0021_customfield_changelog_data.py +0 -1
- nautobot/extras/migrations/0022_objectchange_object_datav2.py +0 -1
- nautobot/extras/migrations/0023_job_model.py +0 -1
- nautobot/extras/migrations/0024_job_data_migration.py +0 -1
- nautobot/extras/migrations/0025_add_advanced_ui_boolean_to_customfield_conputedfield_and_relationship.py +0 -1
- nautobot/extras/migrations/0026_job_add_gitrepository_fk.py +0 -1
- nautobot/extras/migrations/0027_job_gitrepository_data_migration.py +0 -1
- nautobot/extras/migrations/0028_job_reduce_source.py +0 -1
- nautobot/extras/migrations/0029_dynamicgroup.py +0 -1
- nautobot/extras/migrations/0030_webhook_alter_unique_together.py +0 -1
- nautobot/extras/migrations/0031_tag_content_types.py +0 -1
- nautobot/extras/migrations/0032_tag_content_types_data_migration.py +0 -1
- nautobot/extras/migrations/0033_add__optimized_indexing.py +0 -1
- nautobot/extras/migrations/0034_alter_fileattachment_mimetype.py +0 -1
- nautobot/extras/migrations/0035_scheduledjob_crontab.py +0 -1
- nautobot/extras/migrations/0036_job_add_has_sensitive_variables.py +0 -1
- nautobot/extras/migrations/0037_configcontextschema__remove_name_unique__create_constraint_unique_name_owner.py +0 -1
- nautobot/extras/migrations/0038_configcontext_locations.py +0 -1
- nautobot/extras/migrations/0039_objectchange__add_change_context.py +0 -1
- nautobot/extras/migrations/0040_dynamicgroup__dynamicgroupmembership.py +0 -1
- nautobot/extras/migrations/0041_jobresult_job_kwargs.py +0 -1
- nautobot/extras/migrations/0042_job__add_is_job_hook_receiver.py +0 -1
- nautobot/extras/migrations/0043_note.py +0 -1
- nautobot/extras/migrations/0044_add_job_hook.py +0 -1
- nautobot/extras/migrations/0045_add_custom_field_slug.py +0 -1
- nautobot/extras/migrations/0046_populate_custom_field_slug_label.py +0 -1
- nautobot/extras/migrations/0047_enforce_custom_field_slug.py +0 -1
- nautobot/extras/migrations/0048_alter_objectchange_change_context_detail.py +0 -1
- nautobot/extras/migrations/0049_alter_tag_slug.py +0 -1
- nautobot/extras/migrations/0050_customfield_grouping.py +0 -1
- nautobot/extras/migrations/0051_add_job_task_queues.py +0 -1
- nautobot/extras/migrations/0052_configcontext_device_redundancy_groups.py +0 -1
- nautobot/extras/migrations/0053_relationship_required_on.py +0 -1
- nautobot/extras/migrations/0054_scheduledjob_kwargs_request_user_change.py +0 -1
- nautobot/extras/migrations/0055_configcontext_dynamic_groups.py +0 -1
- nautobot/extras/migrations/0056_objectchange_add_reverse_time_idx.py +0 -1
- nautobot/extras/migrations/0057_jobbutton.py +0 -1
- nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
- nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -2
- nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -2
- nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -8
- nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -33
- nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -2
- nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -2
- nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
- nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -2
- nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -3
- nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -2
- nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -2
- nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
- nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -2
- nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
- nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -10
- nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -2
- nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
- nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
- nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
- nautobot/extras/migrations/0078_remove_slug.py +45 -0
- nautobot/extras/migrations/0079_tagsfield.py +28 -0
- nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
- nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
- nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
- nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
- nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
- nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
- nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
- nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
- nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
- nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
- nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
- nautobot/extras/models/__init__.py +2 -3
- nautobot/extras/models/change_logging.py +0 -36
- nautobot/extras/models/customfields.py +39 -33
- nautobot/extras/models/datasources.py +48 -50
- nautobot/extras/models/groups.py +5 -12
- nautobot/extras/models/jobs.py +190 -323
- nautobot/extras/models/mixins.py +0 -71
- nautobot/extras/models/models.py +1 -22
- nautobot/extras/models/relationships.py +20 -21
- nautobot/extras/models/roles.py +0 -23
- nautobot/extras/models/secrets.py +2 -31
- nautobot/extras/models/statuses.py +6 -5
- nautobot/extras/models/tags.py +2 -17
- nautobot/extras/navigation.py +89 -307
- nautobot/extras/plugins/__init__.py +3 -121
- nautobot/extras/plugins/utils.py +0 -3
- nautobot/extras/plugins/validators.py +5 -4
- nautobot/extras/plugins/views.py +16 -4
- nautobot/extras/querysets.py +1 -7
- nautobot/extras/registry.py +3 -0
- nautobot/extras/signals.py +26 -60
- nautobot/extras/tables.py +42 -49
- nautobot/extras/tasks.py +0 -12
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +16 -1
- nautobot/extras/templates/extras/customfield.html +0 -13
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -1
- nautobot/extras/templates/extras/gitrepository.html +3 -3
- nautobot/extras/templates/extras/inc/jobresult.html +10 -0
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
- nautobot/extras/templates/extras/job.html +35 -25
- nautobot/extras/templates/extras/job_approval_request.html +15 -30
- nautobot/extras/templates/extras/job_detail.html +13 -31
- nautobot/extras/templates/extras/job_edit.html +14 -17
- nautobot/extras/templates/extras/jobresult.html +24 -6
- nautobot/extras/templates/extras/objectchange_list.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +2 -2
- nautobot/extras/templates/extras/secret.html +28 -0
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -1
- nautobot/extras/templatetags/custom_links.py +0 -2
- nautobot/extras/templatetags/job_buttons.py +1 -0
- nautobot/extras/templatetags/plugins.py +0 -1
- nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
- nautobot/extras/test_jobs/atomic_transaction.py +53 -0
- nautobot/extras/test_jobs/dry_run.py +29 -0
- nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
- nautobot/extras/test_jobs/duplicate_name2.py +9 -0
- nautobot/extras/test_jobs/fail.py +23 -0
- nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
- nautobot/extras/test_jobs/file_upload_pass.py +25 -0
- nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
- nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
- nautobot/extras/test_jobs/job_button_receiver.py +28 -0
- nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
- nautobot/extras/test_jobs/job_variables.py +88 -0
- nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
- nautobot/extras/test_jobs/log_redaction.py +20 -0
- nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
- nautobot/extras/test_jobs/modify_db.py +25 -0
- nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
- nautobot/extras/test_jobs/object_var_optional.py +21 -0
- nautobot/extras/test_jobs/object_var_required.py +21 -0
- nautobot/extras/test_jobs/object_vars.py +26 -0
- nautobot/extras/test_jobs/pass.py +25 -0
- nautobot/extras/test_jobs/profiling.py +32 -0
- nautobot/extras/test_jobs/read_only_job.py +15 -0
- nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
- nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
- nautobot/extras/tests/integration/__init__.py +3 -3
- nautobot/extras/tests/integration/test_computedfields.py +1 -1
- nautobot/extras/tests/integration/test_configcontextschema.py +7 -5
- nautobot/extras/tests/integration/test_customfields.py +4 -2
- nautobot/extras/tests/integration/test_dynamicgroups.py +2 -2
- nautobot/extras/tests/integration/test_jobs.py +25 -27
- nautobot/extras/tests/integration/test_notes.py +8 -4
- nautobot/extras/tests/integration/test_plugins.py +4 -4
- nautobot/extras/tests/integration/test_relationships.py +2 -2
- nautobot/extras/tests/test_api.py +371 -381
- nautobot/extras/tests/test_changelog.py +17 -16
- nautobot/extras/tests/test_context_managers.py +5 -6
- nautobot/extras/tests/test_customfields.py +112 -73
- nautobot/extras/tests/test_datasources.py +191 -117
- nautobot/extras/tests/test_dynamicgroups.py +45 -68
- nautobot/extras/tests/test_filters.py +170 -130
- nautobot/extras/tests/test_forms.py +107 -109
- nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
- nautobot/extras/tests/test_jobs.py +271 -273
- nautobot/extras/tests/test_management.py +3 -6
- nautobot/extras/tests/test_migrations.py +5 -3
- nautobot/extras/tests/test_models.py +121 -173
- nautobot/extras/tests/test_notes.py +0 -1
- nautobot/extras/tests/test_plugins.py +55 -89
- nautobot/extras/tests/test_relationships.py +174 -130
- nautobot/extras/tests/test_tags.py +6 -12
- nautobot/extras/tests/test_utils.py +31 -1
- nautobot/extras/tests/test_views.py +223 -184
- nautobot/extras/tests/test_webhooks.py +16 -15
- nautobot/extras/urls.py +69 -69
- nautobot/extras/utils.py +137 -163
- nautobot/extras/views.py +81 -153
- nautobot/ipam/api/fields.py +17 -0
- nautobot/ipam/api/serializers.py +77 -164
- nautobot/ipam/api/urls.py +4 -1
- nautobot/ipam/api/views.py +28 -19
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/choices.py +5 -12
- nautobot/ipam/constants.py +1 -0
- nautobot/ipam/factory.py +41 -30
- nautobot/ipam/filters.py +58 -25
- nautobot/ipam/forms.py +82 -211
- nautobot/ipam/graphql/types.py +0 -9
- nautobot/ipam/lookups.py +13 -8
- nautobot/ipam/management/commands/__init__.py +0 -0
- nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
- nautobot/ipam/migrations/0001_initial_part_1.py +0 -1
- nautobot/ipam/migrations/0002_initial_part_2.py +0 -1
- nautobot/ipam/migrations/0003_remove_max_length.py +0 -1
- nautobot/ipam/migrations/0004_fixup_p2p_broadcast.py +0 -1
- nautobot/ipam/migrations/0005_auto_slug.py +0 -1
- nautobot/ipam/migrations/0006_ipaddress_nat_outside_list.py +0 -1
- nautobot/ipam/migrations/0007_add_natural_indexing.py +0 -1
- nautobot/ipam/migrations/0008_prefix_vlan_vlangroup_location.py +0 -1
- nautobot/ipam/migrations/0009_alter_vlan_name.py +0 -1
- nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -2
- nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -39
- nautobot/ipam/migrations/0012_rename_ipam_roles.py +0 -1
- nautobot/ipam/migrations/0013_delete_role.py +0 -1
- nautobot/ipam/migrations/0014_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/ipam/migrations/0015_prefix_add_type.py +0 -1
- nautobot/ipam/migrations/0016_prefix_type_data_migration.py +0 -3
- nautobot/ipam/migrations/0017_prefix_remove_is_pool.py +0 -1
- nautobot/ipam/migrations/0018_remove_site_foreign_key_from_ipam_models.py +0 -1
- nautobot/ipam/migrations/0019_created_datetime.py +0 -1
- nautobot/ipam/migrations/0020_related_name_changes.py +1 -2
- nautobot/ipam/migrations/0021_prefix_add_rir_and_date_allocated.py +0 -1
- nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +3 -5
- nautobot/ipam/migrations/0023_delete_aggregate.py +0 -1
- nautobot/ipam/migrations/0024_interface_to_ipaddress_m2m.py +0 -1
- nautobot/ipam/migrations/0025_interface_ipaddress_m2m_data_migration.py +0 -1
- nautobot/ipam/migrations/0026_ipaddress_remove_assigned_object.py +0 -1
- nautobot/ipam/migrations/0027_remove_rir_slug.py +16 -0
- nautobot/ipam/migrations/0028_tagsfield.py +44 -0
- nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
- nautobot/ipam/migrations/0030_ipam__namespaces.py +231 -0
- nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
- nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
- nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
- nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
- nautobot/ipam/models.py +579 -368
- nautobot/ipam/navigation.py +36 -159
- nautobot/ipam/querysets.py +117 -90
- nautobot/ipam/signals.py +89 -0
- nautobot/ipam/tables.py +86 -28
- nautobot/ipam/templates/ipam/ipaddress.html +14 -30
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -0
- nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
- nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
- nautobot/ipam/templates/ipam/namespace_retrieve.html +42 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
- nautobot/ipam/templates/ipam/prefix.html +27 -33
- nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
- nautobot/ipam/templates/ipam/vlangroup.html +0 -13
- nautobot/ipam/templates/ipam/vrf.html +6 -4
- nautobot/ipam/templates/ipam/vrf_edit.html +20 -2
- nautobot/ipam/tests/integration/test_prefixes.py +4 -27
- nautobot/ipam/tests/test_api.py +60 -61
- nautobot/ipam/tests/test_filters.py +187 -126
- nautobot/ipam/tests/test_forms.py +12 -6
- nautobot/ipam/tests/test_graphql.py +8 -6
- nautobot/ipam/tests/test_migrations.py +8 -13
- nautobot/ipam/tests/test_models.py +426 -274
- nautobot/ipam/tests/test_ordering.py +6 -3
- nautobot/ipam/tests/test_querysets.py +340 -96
- nautobot/ipam/tests/test_views.py +100 -55
- nautobot/ipam/urls.py +28 -5
- nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
- nautobot/ipam/utils/migrations.py +713 -0
- nautobot/ipam/views.py +237 -122
- nautobot/project-static/docs/404.html +1399 -166
- nautobot/project-static/docs/additional-features/caching.html +1416 -320
- nautobot/project-static/docs/additional-features/change-logging.html +1389 -190
- nautobot/project-static/docs/additional-features/config-contexts.html +1389 -190
- nautobot/project-static/docs/additional-features/graphql.html +1389 -190
- nautobot/project-static/docs/additional-features/healthcheck.html +1389 -190
- nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1393 -190
- nautobot/project-static/docs/additional-features/jobs.html +1677 -460
- nautobot/project-static/docs/additional-features/napalm.html +1389 -190
- nautobot/project-static/docs/additional-features/prometheus-metrics.html +1389 -190
- nautobot/project-static/docs/additional-features/template-filters.html +1389 -190
- nautobot/project-static/docs/administration/celery-queues.html +1389 -190
- nautobot/project-static/docs/administration/nautobot-server.html +1553 -375
- nautobot/project-static/docs/administration/nautobot-shell.html +1395 -196
- nautobot/project-static/docs/administration/permissions.html +1389 -190
- nautobot/project-static/docs/administration/replicating-nautobot.html +1387 -207
- nautobot/project-static/docs/apps/index.html +1389 -190
- nautobot/project-static/docs/apps/nautobot-apps.html +1387 -175
- nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js +29 -0
- nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js.map +8 -0
- nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js → search.208ed371.min.js} +9 -15
- nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js.map → search.208ed371.min.js.map} +4 -4
- nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css.map +1 -0
- nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1775 -590
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3588 -1922
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1461 -262
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1401 -170
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1396 -191
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2095 -894
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2357 -1194
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2258 -940
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1400 -201
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11068 -7861
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2867 -2224
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2641 -1573
- nautobot/project-static/docs/configuration/authentication/ldap.html +1389 -190
- nautobot/project-static/docs/configuration/authentication/remote.html +1389 -190
- nautobot/project-static/docs/configuration/authentication/sso.html +1389 -190
- nautobot/project-static/docs/configuration/index.html +1398 -199
- nautobot/project-static/docs/configuration/optional-settings.html +1418 -274
- nautobot/project-static/docs/configuration/required-settings.html +1419 -287
- nautobot/project-static/docs/core-functionality/circuits.html +1446 -247
- nautobot/project-static/docs/core-functionality/device-types.html +1448 -249
- nautobot/project-static/docs/core-functionality/devices.html +1452 -249
- nautobot/project-static/docs/core-functionality/ipam.html +1452 -253
- nautobot/project-static/docs/core-functionality/power.html +1448 -249
- nautobot/project-static/docs/core-functionality/secrets.html +1448 -249
- nautobot/project-static/docs/core-functionality/services.html +1448 -249
- nautobot/project-static/docs/core-functionality/sites-and-racks.html +1448 -249
- nautobot/project-static/docs/core-functionality/tenancy.html +1448 -249
- nautobot/project-static/docs/core-functionality/virtualization.html +1452 -249
- nautobot/project-static/docs/core-functionality/vlans.html +1448 -249
- nautobot/project-static/docs/development/application-registry.html +1393 -214
- nautobot/project-static/docs/development/best-practices.html +1392 -192
- nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1390 -191
- nautobot/project-static/docs/development/extending-models.html +1443 -257
- nautobot/project-static/docs/development/generic-views.html +1403 -174
- nautobot/project-static/docs/development/getting-started.html +1568 -262
- nautobot/project-static/docs/development/homepage.html +1389 -190
- nautobot/project-static/docs/development/index.html +1389 -190
- nautobot/project-static/docs/development/model-features.html +1389 -190
- nautobot/project-static/docs/development/natural-keys.html +1389 -190
- nautobot/project-static/docs/development/navigation-menu.html +1451 -330
- nautobot/project-static/docs/development/react-ui.html +4199 -0
- nautobot/project-static/docs/development/release-checklist.html +1392 -193
- nautobot/project-static/docs/development/role-internals.html +1402 -172
- nautobot/project-static/docs/development/style-guide.html +1399 -199
- nautobot/project-static/docs/development/templates.html +1391 -191
- nautobot/project-static/docs/development/testing.html +1389 -190
- nautobot/project-static/docs/development/user-preferences.html +1389 -190
- nautobot/project-static/docs/docker/index.html +1408 -206
- nautobot/project-static/docs/index.html +1397 -180
- nautobot/project-static/docs/installation/centos.html +1401 -170
- nautobot/project-static/docs/installation/external-authentication.html +1389 -190
- nautobot/project-static/docs/installation/http-server.html +1389 -190
- nautobot/project-static/docs/installation/index.html +1394 -191
- nautobot/project-static/docs/installation/migrating-from-netbox.html +1452 -305
- nautobot/project-static/docs/installation/migrating-from-postgresql.html +1390 -191
- nautobot/project-static/docs/installation/nautobot.html +1390 -191
- nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1389 -190
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +1401 -170
- nautobot/project-static/docs/installation/services.html +1389 -190
- nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/project-static/docs/installation/ubuntu.html +1401 -170
- nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +4254 -1923
- nautobot/project-static/docs/installation/upgrading.html +1395 -192
- nautobot/project-static/docs/models/circuits/circuit.html +1427 -174
- nautobot/project-static/docs/models/circuits/circuittermination.html +1427 -174
- nautobot/project-static/docs/models/circuits/circuittype.html +1427 -174
- nautobot/project-static/docs/models/circuits/provider.html +1427 -174
- nautobot/project-static/docs/models/circuits/providernetwork.html +1427 -174
- nautobot/project-static/docs/models/dcim/cable.html +1458 -174
- nautobot/project-static/docs/models/dcim/consoleport.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleserverport.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/device.html +1431 -174
- nautobot/project-static/docs/models/dcim/devicebay.html +1427 -174
- nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1522 -177
- nautobot/project-static/docs/models/dcim/devicetype.html +1427 -174
- nautobot/project-static/docs/models/dcim/frontport.html +1427 -174
- nautobot/project-static/docs/models/dcim/frontporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/interface.html +1427 -174
- nautobot/project-static/docs/models/dcim/interfacetemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/inventoryitem.html +1427 -174
- nautobot/project-static/docs/models/dcim/location.html +1427 -174
- nautobot/project-static/docs/models/dcim/locationtype.html +1427 -174
- nautobot/project-static/docs/models/dcim/manufacturer.html +1427 -174
- nautobot/project-static/docs/models/dcim/platform.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerfeed.html +1425 -172
- nautobot/project-static/docs/models/dcim/poweroutlet.html +1427 -174
- nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerpanel.html +1425 -172
- nautobot/project-static/docs/models/dcim/powerport.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/rack.html +1427 -174
- nautobot/project-static/docs/models/dcim/rackgroup.html +1427 -174
- nautobot/project-static/docs/models/dcim/rackreservation.html +1427 -174
- nautobot/project-static/docs/models/dcim/rearport.html +1427 -174
- nautobot/project-static/docs/models/dcim/rearporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/region.html +1401 -170
- nautobot/project-static/docs/models/dcim/site.html +1401 -170
- nautobot/project-static/docs/models/dcim/virtualchassis.html +1425 -172
- nautobot/project-static/docs/models/extras/computedfield.html +1393 -194
- nautobot/project-static/docs/models/extras/configcontext.html +1465 -174
- nautobot/project-static/docs/models/extras/configcontextschema.html +1421 -168
- nautobot/project-static/docs/models/extras/customfield.html +1389 -190
- nautobot/project-static/docs/models/extras/customlink.html +1389 -190
- nautobot/project-static/docs/models/extras/dynamicgroup.html +1398 -199
- nautobot/project-static/docs/models/extras/exporttemplate.html +1389 -190
- nautobot/project-static/docs/models/extras/gitrepository.html +1393 -190
- nautobot/project-static/docs/models/extras/graphqlquery.html +1469 -171
- nautobot/project-static/docs/models/extras/imageattachment.html +1434 -181
- nautobot/project-static/docs/models/extras/job.html +1411 -157
- nautobot/project-static/docs/models/extras/jobbutton.html +1410 -207
- nautobot/project-static/docs/models/extras/jobhook.html +1397 -194
- nautobot/project-static/docs/models/extras/joblogentry.html +1408 -155
- nautobot/project-static/docs/models/extras/jobresult.html +1417 -159
- nautobot/project-static/docs/models/extras/note.html +1389 -190
- nautobot/project-static/docs/models/extras/relationship.html +1391 -192
- nautobot/project-static/docs/models/extras/role.html +1495 -198
- nautobot/project-static/docs/models/extras/secret.html +1492 -201
- nautobot/project-static/docs/models/extras/secretsgroup.html +1410 -157
- nautobot/project-static/docs/models/extras/status.html +1381 -221
- nautobot/project-static/docs/models/extras/tag.html +1389 -190
- nautobot/project-static/docs/models/extras/webhook.html +1389 -190
- nautobot/project-static/docs/models/ipam/ipaddress.html +1488 -200
- nautobot/project-static/docs/models/ipam/prefix.html +1410 -157
- nautobot/project-static/docs/models/ipam/rir.html +1410 -157
- nautobot/project-static/docs/models/ipam/routetarget.html +1410 -157
- nautobot/project-static/docs/models/ipam/service.html +1410 -157
- nautobot/project-static/docs/models/ipam/vlan.html +1410 -157
- nautobot/project-static/docs/models/ipam/vlangroup.html +1410 -157
- nautobot/project-static/docs/models/ipam/vrf.html +1410 -161
- nautobot/project-static/docs/models/tenancy/tenant.html +1412 -159
- nautobot/project-static/docs/models/tenancy/tenantgroup.html +1412 -159
- nautobot/project-static/docs/models/users/objectpermission.html +1462 -171
- nautobot/project-static/docs/models/users/token.html +1410 -157
- nautobot/project-static/docs/models/virtualization/cluster.html +1410 -157
- nautobot/project-static/docs/models/virtualization/clustergroup.html +1410 -157
- nautobot/project-static/docs/models/virtualization/clustertype.html +1410 -157
- nautobot/project-static/docs/models/virtualization/virtualmachine.html +1414 -157
- nautobot/project-static/docs/models/virtualization/vminterface.html +1410 -157
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/plugins/development.html +1916 -646
- nautobot/project-static/docs/plugins/index.html +1389 -190
- nautobot/project-static/docs/plugins/porting-from-netbox.html +1389 -190
- nautobot/project-static/docs/release-notes/index.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.0.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.1.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.2.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.3.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.4.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.5.html +2016 -397
- nautobot/project-static/docs/release-notes/version-2.0.html +1935 -287
- nautobot/project-static/docs/requirements.txt +5 -4
- nautobot/project-static/docs/rest-api/authentication.html +1389 -190
- nautobot/project-static/docs/rest-api/filtering.html +1389 -190
- nautobot/project-static/docs/rest-api/overview.html +2002 -576
- nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +197 -187
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guides/custom-fields.html +1390 -191
- nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1392 -193
- nautobot/project-static/docs/user-guides/getting-started/index.html +1388 -189
- nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1388 -189
- nautobot/project-static/docs/user-guides/getting-started/ipam.html +1386 -187
- nautobot/project-static/docs/user-guides/getting-started/platforms.html +1448 -249
- nautobot/project-static/docs/user-guides/getting-started/regions.html +1411 -212
- nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1395 -196
- nautobot/project-static/docs/user-guides/getting-started/tenants.html +1448 -249
- nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1448 -249
- nautobot/project-static/docs/user-guides/git-data-source.html +1405 -206
- nautobot/project-static/docs/user-guides/graphql.html +1402 -203
- nautobot/project-static/docs/user-guides/relationships.html +1448 -249
- nautobot/project-static/docs/user-guides/s3-django-storage.html +1448 -249
- nautobot/project-static/js/forms.js +16 -9
- nautobot/project-static/js/theme.js +5 -0
- nautobot/tenancy/api/serializers.py +4 -34
- nautobot/tenancy/api/urls.py +1 -1
- nautobot/tenancy/filters/__init__.py +9 -7
- nautobot/tenancy/filters/mixins.py +3 -2
- nautobot/tenancy/forms.py +3 -36
- nautobot/tenancy/migrations/0001_initial.py +0 -1
- nautobot/tenancy/migrations/0002_auto_slug.py +0 -1
- nautobot/tenancy/migrations/0003_mptt_to_tree_queries.py +0 -1
- nautobot/tenancy/migrations/0004_change_tree_manager_on_tree_models.py +0 -1
- nautobot/tenancy/migrations/0005_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/tenancy/migrations/0006_created_datetime.py +0 -1
- nautobot/tenancy/migrations/0007_remove_tenant_tenantgroup_slug.py +20 -0
- nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
- nautobot/tenancy/models.py +0 -30
- nautobot/tenancy/navigation.py +6 -39
- nautobot/tenancy/tables.py +4 -4
- nautobot/tenancy/templates/tenancy/tenant.html +12 -12
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -1
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/tenancy/tests/test_api.py +1 -12
- nautobot/tenancy/tests/test_filters.py +20 -12
- nautobot/tenancy/tests/test_views.py +11 -29
- nautobot/tenancy/urls.py +10 -10
- nautobot/tenancy/views.py +0 -3
- nautobot/ui/.eslintignore +6 -0
- nautobot/ui/.gitignore +10 -0
- nautobot/ui/.prettierignore +9 -0
- nautobot/ui/.prettierrc +4 -0
- nautobot/ui/README.md +33 -0
- nautobot/ui/app_imports.js.j2 +7 -0
- nautobot/ui/craco.config.js +46 -0
- nautobot/ui/jsconfig-base.json +11 -0
- nautobot/ui/jsconfig.json +5 -0
- nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
- nautobot/ui/package-lock.json +21451 -0
- nautobot/ui/package.json +70 -0
- nautobot/ui/public/index.html +47 -0
- nautobot/ui/public/logo192.png +0 -0
- nautobot/ui/public/logo512.png +0 -0
- nautobot/ui/public/manifest.json +25 -0
- nautobot/ui/public/nautobot_logo.svg +131 -0
- nautobot/ui/public/robots.txt +3 -0
- nautobot/ui/src/App.js +71 -0
- nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
- nautobot/ui/src/components/AppTab.js +40 -0
- nautobot/ui/src/components/Apps.js +60 -0
- nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
- nautobot/ui/src/components/HomePanel.js +58 -0
- nautobot/ui/src/components/JobHistoryTable.js +78 -0
- nautobot/ui/src/components/Layout.js +53 -0
- nautobot/ui/src/components/LoadingWidget.js +25 -0
- nautobot/ui/src/components/Navbar.js +116 -0
- nautobot/ui/src/components/NotificationPopover.js +27 -0
- nautobot/ui/src/components/ObjectListTable.js +209 -0
- nautobot/ui/src/components/ReferenceDataTag.js +35 -0
- nautobot/ui/src/components/RouterButton.js +10 -0
- nautobot/ui/src/components/RouterLink.js +10 -0
- nautobot/ui/src/components/SidebarNav.js +147 -0
- nautobot/ui/src/components/Table.js +48 -0
- nautobot/ui/src/components/TableItem.js +71 -0
- nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
- nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
- nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
- nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
- nautobot/ui/src/components/__tests__/Table.test.js +36 -0
- nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
- nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
- nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
- nautobot/ui/src/components/pagination.js +93 -0
- nautobot/ui/src/components/paginator.js +79 -0
- nautobot/ui/src/components/paginator_form.js +43 -0
- nautobot/ui/src/components/usePagination.js +57 -0
- nautobot/ui/src/constants/apiPath.js +10 -0
- nautobot/ui/src/constants/icons.js +15 -0
- nautobot/ui/src/constants/size.js +15 -0
- nautobot/ui/src/index.js +65 -0
- nautobot/ui/src/reportWebVitals.js +15 -0
- nautobot/ui/src/router.js +77 -0
- nautobot/ui/src/utils/api.js +131 -0
- nautobot/ui/src/utils/app-import.js +15 -0
- nautobot/ui/src/utils/color.js +15 -0
- nautobot/ui/src/utils/date.js +14 -0
- nautobot/ui/src/utils/index.js +15 -0
- nautobot/ui/src/utils/navigation.js +32 -0
- nautobot/ui/src/utils/session.js +64 -0
- nautobot/ui/src/utils/store.js +242 -0
- nautobot/ui/src/utils/string.js +6 -0
- nautobot/ui/src/utils/url.js +4 -0
- nautobot/ui/src/views/Home.js +138 -0
- nautobot/ui/src/views/InstalledApps.js +80 -0
- nautobot/ui/src/views/Login.js +48 -0
- nautobot/ui/src/views/Logout.js +20 -0
- nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
- nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
- nautobot/ui/src/views/__tests__/Login.test.js +15 -0
- nautobot/ui/src/views/generic/GenericView.js +142 -0
- nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
- nautobot/ui/src/views/generic/ObjectList.js +127 -0
- nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +51 -61
- nautobot/users/api/urls.py +1 -1
- nautobot/users/api/views.py +53 -2
- nautobot/users/migrations/0001_initial.py +0 -1
- nautobot/users/migrations/0002_token_ordering_by_created.py +0 -1
- nautobot/users/migrations/0003_alter_user_options.py +0 -1
- nautobot/users/migrations/0004_alter_user_managers.py +0 -1
- nautobot/users/tests/test_api.py +109 -28
- nautobot/users/tests/test_filters.py +0 -4
- nautobot/users/tests/test_models.py +0 -1
- nautobot/users/views.py +0 -7
- nautobot/virtualization/api/serializers.py +18 -132
- nautobot/virtualization/api/urls.py +1 -1
- nautobot/virtualization/api/views.py +1 -22
- nautobot/virtualization/choices.py +0 -2
- nautobot/virtualization/filters.py +12 -7
- nautobot/virtualization/forms.py +21 -117
- nautobot/virtualization/migrations/0001_initial.py +0 -1
- nautobot/virtualization/migrations/0002_virtualmachine_local_context_schema.py +0 -1
- nautobot/virtualization/migrations/0003_vminterface_verbose_name.py +0 -1
- nautobot/virtualization/migrations/0004_auto_slug.py +0 -1
- nautobot/virtualization/migrations/0005_add_natural_indexing.py +0 -1
- nautobot/virtualization/migrations/0006_vminterface_status.py +0 -1
- nautobot/virtualization/migrations/0007_vminterface_status_data_migration.py +0 -1
- nautobot/virtualization/migrations/0008_vminterface_parent.py +0 -1
- nautobot/virtualization/migrations/0009_cluster_location.py +0 -1
- nautobot/virtualization/migrations/0010_vminterface_mac_address_data_migration.py +0 -1
- nautobot/virtualization/migrations/0011_alter_vminterface_mac_address.py +0 -1
- nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -2
- nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -12
- nautobot/virtualization/migrations/0014_rename_virtualmachine_roles.py +0 -1
- nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -2
- nautobot/virtualization/migrations/0016_remove_site_foreign_key_from_cluster_class.py +0 -1
- nautobot/virtualization/migrations/0017_created_datetime.py +0 -1
- nautobot/virtualization/migrations/0018_related_name_changes.py +1 -2
- nautobot/virtualization/migrations/0019_vminterface_ip_addresses_m2m.py +0 -1
- nautobot/virtualization/migrations/0020_remove_clustergroup_clustertype_slug.py +20 -0
- nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
- nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
- nautobot/virtualization/migrations/0023_ipam__namespaces.py +25 -0
- nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
- nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
- nautobot/virtualization/models.py +39 -131
- nautobot/virtualization/navigation.py +18 -99
- nautobot/virtualization/tables.py +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine.html +13 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
- nautobot/virtualization/tests/test_api.py +42 -52
- nautobot/virtualization/tests/test_filters.py +98 -75
- nautobot/virtualization/tests/test_models.py +36 -13
- nautobot/virtualization/tests/test_views.py +68 -73
- nautobot/virtualization/urls.py +10 -10
- nautobot/virtualization/views.py +8 -14
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +15 -22
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +987 -834
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
- nautobot/circuits/api/nested_serializers.py +0 -69
- nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
- nautobot/dcim/api/nested_serializers.py +0 -356
- nautobot/dcim/templates/dcim/device_import.html +0 -5
- nautobot/dcim/templates/dcim/device_import_child.html +0 -5
- nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
- nautobot/extras/api/nested_serializers.py +0 -353
- nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -42
- nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -47
- nautobot/extras/reports.py +0 -60
- nautobot/extras/scripts.py +0 -72
- nautobot/extras/tests/example_jobs/script_variables.py +0 -67
- nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
- nautobot/extras/tests/example_jobs/test_fail.py +0 -16
- nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
- nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
- nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
- nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
- nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
- nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
- nautobot/extras/tests/example_jobs/test_modify_db.py +0 -19
- nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
- nautobot/extras/tests/example_jobs/test_pass.py +0 -19
- nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
- nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
- nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
- nautobot/ipam/api/nested_serializers.py +0 -143
- nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js +0 -29
- nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js.map +0 -8
- nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js +0 -18
- nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js.map +0 -8
- nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css.map +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css.map +0 -1
- nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css.map +0 -1
- nautobot/tenancy/api/nested_serializers.py +0 -31
- nautobot/users/api/nested_serializers.py +0 -67
- nautobot/virtualization/api/nested_serializers.py +0 -65
- /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
- /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
nautobot/core/views/generic.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from copy import deepcopy
|
|
2
|
+
from io import BytesIO
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
4
5
|
|
|
@@ -12,16 +13,18 @@ from django.core.exceptions import (
|
|
|
12
13
|
)
|
|
13
14
|
from django.db import transaction, IntegrityError
|
|
14
15
|
from django.db.models import ManyToManyField, ProtectedError
|
|
15
|
-
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
16
|
-
from django.http import HttpResponse
|
|
16
|
+
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
17
|
+
from django.http import HttpResponse, JsonResponse
|
|
17
18
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
18
|
-
from django.urls import NoReverseMatch, reverse
|
|
19
19
|
from django.utils.html import escape
|
|
20
20
|
from django.utils.http import is_safe_url
|
|
21
21
|
from django.utils.safestring import mark_safe
|
|
22
22
|
from django.views.generic import View
|
|
23
23
|
from django_tables2 import RequestConfig
|
|
24
|
+
from rest_framework.exceptions import ParseError
|
|
24
25
|
|
|
26
|
+
from nautobot.core.api.parsers import NautobotCSVParser
|
|
27
|
+
from nautobot.core.api.utils import get_serializer_for_model
|
|
25
28
|
from nautobot.core.forms import SearchForm
|
|
26
29
|
from nautobot.core.exceptions import AbortTransaction
|
|
27
30
|
from nautobot.core.forms import (
|
|
@@ -36,7 +39,6 @@ from nautobot.core.forms import (
|
|
|
36
39
|
)
|
|
37
40
|
from nautobot.core.forms.forms import DynamicFilterFormSet
|
|
38
41
|
from nautobot.core.templatetags.helpers import bettertitle, validated_viewname
|
|
39
|
-
from nautobot.core.utils.lookup import get_route_for_model
|
|
40
42
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
41
43
|
from nautobot.core.utils.requests import (
|
|
42
44
|
convert_querydict_to_factory_formset_acceptable_querydict,
|
|
@@ -45,9 +47,13 @@ from nautobot.core.utils.requests import (
|
|
|
45
47
|
)
|
|
46
48
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
47
49
|
from nautobot.core.views.mixins import GetReturnURLMixin, ObjectPermissionRequiredMixin
|
|
48
|
-
from nautobot.core.views.utils import
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
from nautobot.core.views.utils import (
|
|
51
|
+
check_filter_for_display,
|
|
52
|
+
get_csv_form_fields_from_serializer_class,
|
|
53
|
+
handle_protectederror,
|
|
54
|
+
prepare_cloned_fields,
|
|
55
|
+
)
|
|
56
|
+
from nautobot.extras.models import ExportTemplate
|
|
51
57
|
from nautobot.extras.utils import remove_prefix_from_cf_key
|
|
52
58
|
|
|
53
59
|
|
|
@@ -61,6 +67,7 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
|
61
67
|
|
|
62
68
|
queryset = None
|
|
63
69
|
template_name = None
|
|
70
|
+
use_new_ui = True
|
|
64
71
|
|
|
65
72
|
def get_required_permission(self):
|
|
66
73
|
return get_permission_for_model(self.queryset.model, "view")
|
|
@@ -89,55 +96,41 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
|
89
96
|
"active_tab": request.GET.get("tab", "main"),
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
# 2.0 TODO: Remove this method in 2.0. Can be retrieved from instance itself now
|
|
93
|
-
# instance.get_changelog_url()
|
|
94
|
-
# Only available on models that support changelogs
|
|
95
|
-
def get_changelog_url(self, instance):
|
|
96
|
-
"""Return the changelog URL for a given instance."""
|
|
97
|
-
meta = self.queryset.model._meta
|
|
98
|
-
|
|
99
|
-
# Don't try to generate a changelog_url for an ObjectChange.
|
|
100
|
-
if meta.model_name == "objectchange":
|
|
101
|
-
return None
|
|
102
|
-
|
|
103
|
-
route = get_route_for_model(instance, "changelog")
|
|
104
|
-
|
|
105
|
-
# Iterate the pk-like fields and try to get a URL, or return None.
|
|
106
|
-
fields = ["pk", "slug"]
|
|
107
|
-
for field in fields:
|
|
108
|
-
if not hasattr(instance, field):
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
try:
|
|
112
|
-
return reverse(route, kwargs={field: getattr(instance, field)})
|
|
113
|
-
except NoReverseMatch:
|
|
114
|
-
continue
|
|
115
|
-
|
|
116
|
-
# This object likely doesn't have a changelog route defined.
|
|
117
|
-
return None
|
|
118
|
-
|
|
119
99
|
def get(self, request, *args, **kwargs):
|
|
120
100
|
"""
|
|
121
101
|
Generic GET handler for accessing an object by PK or slug
|
|
122
102
|
"""
|
|
123
103
|
instance = get_object_or_404(self.queryset, **kwargs)
|
|
124
104
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
105
|
+
# TODO: this feels inelegant - should the tabs lookup be a dedicated endpoint rather than piggybacking
|
|
106
|
+
# on the object-retrieve endpoint?
|
|
107
|
+
# TODO: similar functionality probably needed in NautobotUIViewSet as well, not currently present
|
|
108
|
+
if request.GET.get("viewconfig", None) == "true":
|
|
109
|
+
# TODO: we shouldn't be importing a private-named function from another module. Should it be renamed?
|
|
110
|
+
from nautobot.extras.templatetags.plugins import _get_registered_content
|
|
129
111
|
|
|
130
|
-
|
|
131
|
-
request,
|
|
132
|
-
self.get_template_name(),
|
|
133
|
-
{
|
|
112
|
+
temp_fake_context = {
|
|
134
113
|
"object": instance,
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
114
|
+
"request": request,
|
|
115
|
+
"settings": {},
|
|
116
|
+
"csrf_token": "",
|
|
117
|
+
"perms": {},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
plugin_tabs = _get_registered_content(instance, "detail_tabs", temp_fake_context, return_html=False)
|
|
121
|
+
resp = {"tabs": plugin_tabs}
|
|
122
|
+
return JsonResponse(resp)
|
|
123
|
+
else:
|
|
124
|
+
return render(
|
|
125
|
+
request,
|
|
126
|
+
self.get_template_name(),
|
|
127
|
+
{
|
|
128
|
+
"object": instance,
|
|
129
|
+
"verbose_name": self.queryset.model._meta.verbose_name,
|
|
130
|
+
"verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
|
|
131
|
+
**self.get_extra_context(request, instance),
|
|
132
|
+
},
|
|
133
|
+
)
|
|
141
134
|
|
|
142
135
|
|
|
143
136
|
class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
@@ -165,15 +158,17 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
165
158
|
"per_page", # used by get_paginate_count
|
|
166
159
|
"sort", # table sorting
|
|
167
160
|
)
|
|
161
|
+
use_new_ui = True
|
|
168
162
|
|
|
169
163
|
def get_filter_params(self, request):
|
|
170
164
|
"""Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
|
|
171
165
|
filter_params = request.GET.copy()
|
|
172
|
-
return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset)
|
|
166
|
+
return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset())
|
|
173
167
|
|
|
174
168
|
def get_required_permission(self):
|
|
175
169
|
return get_permission_for_model(self.queryset.model, "view")
|
|
176
170
|
|
|
171
|
+
# TODO: remove this as well?
|
|
177
172
|
def queryset_to_yaml(self):
|
|
178
173
|
"""
|
|
179
174
|
Export the queryset of objects as concatenated YAML documents.
|
|
@@ -182,35 +177,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
182
177
|
|
|
183
178
|
return "---\n".join(yaml_data)
|
|
184
179
|
|
|
185
|
-
def queryset_to_csv(self):
|
|
186
|
-
"""
|
|
187
|
-
Export the queryset of objects as comma-separated value (CSV), using the model's to_csv() method.
|
|
188
|
-
"""
|
|
189
|
-
csv_data = []
|
|
190
|
-
custom_field_keys = []
|
|
191
|
-
|
|
192
|
-
# Start with the column headers
|
|
193
|
-
headers = self.queryset.model.csv_headers.copy()
|
|
194
|
-
|
|
195
|
-
# Add custom field headers, if any
|
|
196
|
-
if hasattr(self.queryset.model, "_custom_field_data"):
|
|
197
|
-
for custom_field in CustomField.objects.get_for_model(self.queryset.model):
|
|
198
|
-
headers.append(custom_field.add_prefix_to_cf_key())
|
|
199
|
-
custom_field_keys.append(custom_field.key)
|
|
200
|
-
|
|
201
|
-
csv_data.append(",".join(headers))
|
|
202
|
-
|
|
203
|
-
# Iterate through the queryset appending each object
|
|
204
|
-
for obj in self.queryset:
|
|
205
|
-
data = obj.to_csv()
|
|
206
|
-
|
|
207
|
-
for custom_field_key in custom_field_keys:
|
|
208
|
-
data += (obj.cf.get(custom_field_key, ""),)
|
|
209
|
-
|
|
210
|
-
csv_data.append(csv_format(data))
|
|
211
|
-
|
|
212
|
-
return "\n".join(csv_data)
|
|
213
|
-
|
|
214
180
|
def validate_action_buttons(self, request):
|
|
215
181
|
"""Verify actions in self.action_buttons are valid view actions."""
|
|
216
182
|
|
|
@@ -240,7 +206,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
240
206
|
if self.filterset:
|
|
241
207
|
filter_params = self.get_filter_params(request)
|
|
242
208
|
filterset = self.filterset(filter_params, self.queryset)
|
|
243
|
-
filterset_filters = filterset.get_filters()
|
|
244
209
|
self.queryset = filterset.qs
|
|
245
210
|
if not filterset.is_valid():
|
|
246
211
|
messages.error(
|
|
@@ -250,17 +215,17 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
250
215
|
self.queryset = self.queryset.none()
|
|
251
216
|
|
|
252
217
|
display_filter_params = [
|
|
253
|
-
check_filter_for_display(
|
|
218
|
+
check_filter_for_display(filterset.filters, field_name, values)
|
|
254
219
|
for field_name, values in filter_params.items()
|
|
255
220
|
]
|
|
256
221
|
|
|
257
222
|
if request.GET:
|
|
258
223
|
factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
|
|
259
|
-
request.GET,
|
|
224
|
+
request.GET, filterset
|
|
260
225
|
)
|
|
261
|
-
dynamic_filter_form = DynamicFilterFormSet(
|
|
226
|
+
dynamic_filter_form = DynamicFilterFormSet(filterset=filterset, data=factory_formset_params)
|
|
262
227
|
else:
|
|
263
|
-
dynamic_filter_form = DynamicFilterFormSet(
|
|
228
|
+
dynamic_filter_form = DynamicFilterFormSet(filterset=filterset)
|
|
264
229
|
|
|
265
230
|
if self.filterset_form:
|
|
266
231
|
filter_form = self.filterset_form(filter_params, label_suffix="")
|
|
@@ -287,13 +252,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
287
252
|
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
|
288
253
|
return response
|
|
289
254
|
|
|
290
|
-
# Fall back to built-in CSV formatting if export requested but no template specified
|
|
291
|
-
elif "export" in request.GET and hasattr(model, "to_csv"):
|
|
292
|
-
response = HttpResponse(self.queryset_to_csv(), content_type="text/csv")
|
|
293
|
-
filename = f"{settings.BRANDING_PREPENDED_FILENAME}{self.queryset.model._meta.verbose_name_plural}.csv"
|
|
294
|
-
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
|
295
|
-
return response
|
|
296
|
-
|
|
297
255
|
# Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
|
|
298
256
|
self.queryset = self.alter_queryset(request)
|
|
299
257
|
|
|
@@ -468,10 +426,58 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
468
426
|
else:
|
|
469
427
|
return redirect(self.get_return_url(request, obj))
|
|
470
428
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
429
|
+
# TODO(jathan): This is a temporary workaround for legacy generic views in support of
|
|
430
|
+
# PrefixEditView/PrefixForm where `unique_together` constraint raises an IntegrityError
|
|
431
|
+
# because `network` & `prefix_length` are excluded from the form and are therefore not
|
|
432
|
+
# passed to `full_clean()`. This results in `form.is_valid()` always returning `True`,
|
|
433
|
+
# and any exceptions end up getting bubbled up when `form.save()` is called. Therefore
|
|
434
|
+
# the error(s) have to be handled here.
|
|
435
|
+
#
|
|
436
|
+
# The problem is that the error from `IntegrityError` is hideous, and looks like this:
|
|
437
|
+
#
|
|
438
|
+
# IntegrityError: duplicate key value violates unique constraint "ipam_prefix_namespace_id_network_prefix_length_b2dd8b57_uniq"
|
|
439
|
+
# DETAIL: Key (namespace_id, network, prefix_length)=(e0b0aa8d-d24d-4312-a2e5-f185c41281e6, \x0a000000, 8) already exists.
|
|
440
|
+
#
|
|
441
|
+
# So, there is a `pre_save` signal handler to forcibly call `Prefix.full_clean` without
|
|
442
|
+
# field exclusions which results in a `ValidationError` that looks pretty, like so:
|
|
443
|
+
#
|
|
444
|
+
# ValidationError: {'__all__': ['Prefix with this Namespace, Network and Prefix length already exists.']}
|
|
445
|
+
#
|
|
446
|
+
# No extra work shall be spent on this as the form validation is being replaced with
|
|
447
|
+
# JSON schema forms in the v2 UI and all of this should go away because validation will
|
|
448
|
+
# be performed by serializers instead. When `serializer.is_valid()` is called, it
|
|
449
|
+
# actually returns `False` and properly shows the errors:
|
|
450
|
+
#
|
|
451
|
+
# >>> serializer.is_valid()
|
|
452
|
+
# False
|
|
453
|
+
#
|
|
454
|
+
# >>> serializer.errors
|
|
455
|
+
# {'__all__': [ErrorDetail(string='Prefix with this Namespace, Network and Prefix length already exists.', code='unique_together')]}
|
|
456
|
+
except ValidationError as err:
|
|
457
|
+
logger.exception(err)
|
|
458
|
+
for field, errors in err.message_dict.items():
|
|
459
|
+
if field == "__all__":
|
|
460
|
+
field = None
|
|
461
|
+
for error in errors:
|
|
462
|
+
form.add_error(field, error)
|
|
463
|
+
# This is necessary due to the uniqueness constraint from the database for `ipam.Prefix`
|
|
464
|
+
# bubbling up. Just a quick fix for the time being without debugging too hard, to know
|
|
465
|
+
# that this at least surfaces the (ugly) error in the UI.
|
|
466
|
+
except IntegrityError as err:
|
|
467
|
+
logger.exception(err)
|
|
468
|
+
models = ", ".join(o.replace("_", " ").title() for o in obj._meta.unique_together[0])
|
|
469
|
+
model_name = obj._meta.verbose_name.title()
|
|
470
|
+
msg = f"{model_name} with this {models} already exists."
|
|
474
471
|
form.add_error(None, msg)
|
|
472
|
+
except ObjectDoesNotExist as err:
|
|
473
|
+
if self.queryset.model._meta.model_name == "ipaddress":
|
|
474
|
+
msg = str(err) + "! Does it exist?"
|
|
475
|
+
field = "address"
|
|
476
|
+
else:
|
|
477
|
+
msg = "Object save failed due to object-level permissions violation"
|
|
478
|
+
field = None
|
|
479
|
+
logger.debug(msg)
|
|
480
|
+
form.add_error(field, msg)
|
|
475
481
|
|
|
476
482
|
else:
|
|
477
483
|
logger.debug("Form validation failed")
|
|
@@ -824,31 +830,31 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
824
830
|
Import objects in bulk (CSV format).
|
|
825
831
|
|
|
826
832
|
queryset: Base queryset for the model
|
|
827
|
-
model_form: The form used to create each imported object
|
|
828
833
|
table: The django-tables2 Table used to render the list of imported objects
|
|
829
834
|
template_name: The name of the template
|
|
830
|
-
widget_attrs: A dict of attributes to apply to the import widget (e.g. to require a session key)
|
|
831
835
|
"""
|
|
832
836
|
|
|
833
837
|
queryset = None
|
|
834
|
-
model_form = None
|
|
835
838
|
table = None
|
|
836
839
|
template_name = "generic/object_bulk_import.html"
|
|
837
|
-
|
|
840
|
+
|
|
841
|
+
def __init__(self, *args, **kwargs):
|
|
842
|
+
super().__init__(*args, **kwargs)
|
|
843
|
+
self.serializer_class = get_serializer_for_model(self.queryset.model)
|
|
844
|
+
self.fields = get_csv_form_fields_from_serializer_class(self.serializer_class)
|
|
845
|
+
self.required_field_names = [
|
|
846
|
+
field["name"]
|
|
847
|
+
for field in get_csv_form_fields_from_serializer_class(self.serializer_class)
|
|
848
|
+
if field["required"]
|
|
849
|
+
]
|
|
838
850
|
|
|
839
851
|
def _import_form(self, *args, **kwargs):
|
|
840
852
|
class CSVImportForm(BootstrapMixin, Form):
|
|
841
|
-
csv_data = CSVDataField(
|
|
842
|
-
csv_file = CSVFileField(
|
|
853
|
+
csv_data = CSVDataField(required_field_names=self.required_field_names)
|
|
854
|
+
csv_file = CSVFileField()
|
|
843
855
|
|
|
844
856
|
return CSVImportForm(*args, **kwargs)
|
|
845
857
|
|
|
846
|
-
def _save_obj(self, obj_form, request):
|
|
847
|
-
"""
|
|
848
|
-
Provide a hook to modify the object immediately before saving it (e.g. to encrypt secret data).
|
|
849
|
-
"""
|
|
850
|
-
return obj_form.save()
|
|
851
|
-
|
|
852
858
|
def get_required_permission(self):
|
|
853
859
|
return get_permission_for_model(self.queryset.model, "add")
|
|
854
860
|
|
|
@@ -858,8 +864,8 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
858
864
|
self.template_name,
|
|
859
865
|
{
|
|
860
866
|
"form": self._import_form(),
|
|
861
|
-
"fields": self.
|
|
862
|
-
"obj_type": self.
|
|
867
|
+
"fields": self.fields,
|
|
868
|
+
"obj_type": self.queryset.model._meta.verbose_name,
|
|
863
869
|
"return_url": self.get_return_url(request),
|
|
864
870
|
"active_tab": "csv-data",
|
|
865
871
|
},
|
|
@@ -880,18 +886,24 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
880
886
|
field_name = "csv_file"
|
|
881
887
|
else:
|
|
882
888
|
field_name = "csv_data"
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
889
|
+
csvtext = form.cleaned_data[field_name]
|
|
890
|
+
|
|
891
|
+
try:
|
|
892
|
+
data = NautobotCSVParser().parse(
|
|
893
|
+
stream=BytesIO(csvtext.encode("utf-8")),
|
|
894
|
+
parser_context={"request": request, "serializer_class": self.serializer_class},
|
|
895
|
+
)
|
|
896
|
+
serializer = self.serializer_class(data=data, context={"request": request}, many=True)
|
|
897
|
+
if serializer.is_valid():
|
|
898
|
+
new_objs = serializer.save()
|
|
891
899
|
else:
|
|
892
|
-
for
|
|
893
|
-
|
|
900
|
+
for row, errors in enumerate(serializer.errors, start=1):
|
|
901
|
+
for field, err in errors.items():
|
|
902
|
+
form.add_error(field_name, f"Row {row}: {field}: {err[0]}")
|
|
894
903
|
raise ValidationError("")
|
|
904
|
+
except ParseError as exc:
|
|
905
|
+
form.add_error(None, str(exc))
|
|
906
|
+
raise ValidationError("")
|
|
895
907
|
|
|
896
908
|
# Enforce object-level permissions
|
|
897
909
|
if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs):
|
|
@@ -930,8 +942,8 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
930
942
|
self.template_name,
|
|
931
943
|
{
|
|
932
944
|
"form": form,
|
|
933
|
-
"fields": self.
|
|
934
|
-
"obj_type": self.
|
|
945
|
+
"fields": self.fields,
|
|
946
|
+
"obj_type": self.queryset.model._meta.verbose_name,
|
|
935
947
|
"return_url": self.get_return_url(request),
|
|
936
948
|
"active_tab": "csv-file" if form.has_error("csv_file") else "csv-data",
|
|
937
949
|
},
|
nautobot/core/views/mixins.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from io import BytesIO
|
|
1
2
|
import logging
|
|
2
3
|
|
|
3
4
|
from django.contrib import messages
|
|
@@ -11,7 +12,7 @@ from django.core.exceptions import (
|
|
|
11
12
|
)
|
|
12
13
|
from django.db import transaction
|
|
13
14
|
from django.db.models import ManyToManyField, ProtectedError
|
|
14
|
-
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
15
|
+
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
15
16
|
from django.http import HttpResponse
|
|
16
17
|
from django.shortcuts import get_object_or_404, redirect
|
|
17
18
|
from django.template.loader import select_template, TemplateDoesNotExist
|
|
@@ -29,7 +30,8 @@ from rest_framework.viewsets import GenericViewSet
|
|
|
29
30
|
|
|
30
31
|
from drf_spectacular.utils import extend_schema
|
|
31
32
|
|
|
32
|
-
from nautobot.core.api.
|
|
33
|
+
from nautobot.core.api.parsers import NautobotCSVParser
|
|
34
|
+
from nautobot.core.api.views import BulkDestroyModelMixin, BulkUpdateModelMixin
|
|
33
35
|
from nautobot.core.forms import (
|
|
34
36
|
BootstrapMixin,
|
|
35
37
|
ConfirmationForm,
|
|
@@ -40,8 +42,12 @@ from nautobot.core.forms import (
|
|
|
40
42
|
from nautobot.core.utils import lookup, permissions
|
|
41
43
|
from nautobot.core.views.renderers import NautobotHTMLRenderer
|
|
42
44
|
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
43
|
-
from nautobot.core.views.utils import
|
|
44
|
-
|
|
45
|
+
from nautobot.core.views.utils import (
|
|
46
|
+
get_csv_form_fields_from_serializer_class,
|
|
47
|
+
handle_protectederror,
|
|
48
|
+
prepare_cloned_fields,
|
|
49
|
+
)
|
|
50
|
+
from nautobot.extras.models import ExportTemplate
|
|
45
51
|
from nautobot.extras.forms import NoteForm
|
|
46
52
|
from nautobot.extras.tables import ObjectChangeTable, NoteTable
|
|
47
53
|
from nautobot.extras.utils import remove_prefix_from_cf_key
|
|
@@ -179,8 +185,12 @@ class GetReturnURLMixin:
|
|
|
179
185
|
# Note that the use of both `obj.present_in_database` and `obj.pk` is correct here because this conditional
|
|
180
186
|
# handles all three of the create, update, and delete operations. When Django deletes an instance
|
|
181
187
|
# from the DB, it sets the instance's PK field to None, regardless of the use of a UUID.
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
try:
|
|
189
|
+
if obj is not None and obj.present_in_database and obj.pk:
|
|
190
|
+
return obj.get_absolute_url()
|
|
191
|
+
except AttributeError:
|
|
192
|
+
# Model has no get_absolute_url() method or no reverse match
|
|
193
|
+
pass
|
|
184
194
|
|
|
185
195
|
# Fall back to the default URL (if specified) for the view.
|
|
186
196
|
if self.default_return_url is not None:
|
|
@@ -205,7 +215,6 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
|
|
|
205
215
|
|
|
206
216
|
renderer_classes = [NautobotHTMLRenderer]
|
|
207
217
|
logger = logging.getLogger(__name__)
|
|
208
|
-
lookup_field = "slug"
|
|
209
218
|
# Attributes that need to be specified: form_class, queryset, serializer_class, table_class for most mixins.
|
|
210
219
|
# filterset and filter_params will be initialized in filter_queryset() in ObjectListViewMixin
|
|
211
220
|
filter_params = None
|
|
@@ -448,7 +457,7 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
|
|
|
448
457
|
def get_filter_params(self, request):
|
|
449
458
|
"""Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
|
|
450
459
|
filter_params = request.GET.copy()
|
|
451
|
-
return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset_class)
|
|
460
|
+
return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset_class())
|
|
452
461
|
|
|
453
462
|
def get_queryset(self):
|
|
454
463
|
"""
|
|
@@ -522,12 +531,15 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
|
|
|
522
531
|
if self.action in ["create", "update"]:
|
|
523
532
|
form_class = getattr(self, "form_class", None)
|
|
524
533
|
elif self.action == "bulk_create":
|
|
534
|
+
required_field_names = [
|
|
535
|
+
field["name"]
|
|
536
|
+
for field in get_csv_form_fields_from_serializer_class(self.serializer_class)
|
|
537
|
+
if field["required"]
|
|
538
|
+
]
|
|
525
539
|
|
|
526
540
|
class BulkCreateForm(BootstrapMixin, Form):
|
|
527
|
-
csv_data = CSVDataField(
|
|
528
|
-
|
|
529
|
-
)
|
|
530
|
-
csv_file = CSVFileField(from_form=self.bulk_create_form_class)
|
|
541
|
+
csv_data = CSVDataField(required_field_names=required_field_names)
|
|
542
|
+
csv_file = CSVFileField()
|
|
531
543
|
|
|
532
544
|
form_class = BulkCreateForm
|
|
533
545
|
else:
|
|
@@ -566,6 +578,17 @@ class ObjectDetailViewMixin(NautobotViewSetMixin, mixins.RetrieveModelMixin):
|
|
|
566
578
|
UI mixin to retrieve a model instance.
|
|
567
579
|
"""
|
|
568
580
|
|
|
581
|
+
def retrieve(self, request, *args, **kwargs):
|
|
582
|
+
"""
|
|
583
|
+
Retrieve a model instance.
|
|
584
|
+
"""
|
|
585
|
+
instance = self.get_object()
|
|
586
|
+
serializer = self.get_serializer(instance)
|
|
587
|
+
|
|
588
|
+
context = serializer.data
|
|
589
|
+
context["use_new_ui"] = True
|
|
590
|
+
return Response(context)
|
|
591
|
+
|
|
569
592
|
|
|
570
593
|
class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
|
|
571
594
|
"""
|
|
@@ -622,13 +645,6 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
|
|
|
622
645
|
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
|
623
646
|
return response
|
|
624
647
|
|
|
625
|
-
# Fall back to built-in CSV formatting if export requested but no template specified
|
|
626
|
-
elif "export" in request.GET and hasattr(model, "to_csv"):
|
|
627
|
-
response = HttpResponse(self.queryset_to_csv(), content_type="text/csv")
|
|
628
|
-
filename = f"nautobot_{queryset.model._meta.verbose_name_plural}.csv"
|
|
629
|
-
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
|
630
|
-
return response
|
|
631
|
-
|
|
632
648
|
return None
|
|
633
649
|
|
|
634
650
|
def queryset_to_yaml(self):
|
|
@@ -640,40 +656,11 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
|
|
|
640
656
|
|
|
641
657
|
return "---\n".join(yaml_data)
|
|
642
658
|
|
|
643
|
-
def queryset_to_csv(self):
|
|
644
|
-
"""
|
|
645
|
-
Export the queryset of objects as comma-separated value (CSV), using the model's to_csv() method.
|
|
646
|
-
"""
|
|
647
|
-
queryset = self.filter_queryset(self.get_queryset())
|
|
648
|
-
csv_data = []
|
|
649
|
-
custom_fields = []
|
|
650
|
-
# Start with the column headers
|
|
651
|
-
headers = queryset.model.csv_headers.copy()
|
|
652
|
-
|
|
653
|
-
# Add custom field headers, if any
|
|
654
|
-
if hasattr(queryset.model, "_custom_field_data"):
|
|
655
|
-
for custom_field in CustomField.objects.get_for_model(queryset.model):
|
|
656
|
-
headers.append(custom_field.add_prefix_to_cf_key())
|
|
657
|
-
custom_fields.append(custom_field.key)
|
|
658
|
-
|
|
659
|
-
csv_data.append(",".join(headers))
|
|
660
|
-
|
|
661
|
-
# Iterate through the queryset appending each object
|
|
662
|
-
for obj in queryset:
|
|
663
|
-
data = obj.to_csv()
|
|
664
|
-
|
|
665
|
-
for custom_field in custom_fields:
|
|
666
|
-
data += (obj.cf.get(custom_field, ""),)
|
|
667
|
-
|
|
668
|
-
csv_data.append(csv_format(data))
|
|
669
|
-
|
|
670
|
-
return "\n".join(csv_data)
|
|
671
|
-
|
|
672
659
|
def list(self, request, *args, **kwargs):
|
|
673
660
|
"""
|
|
674
661
|
List the model instances.
|
|
675
662
|
"""
|
|
676
|
-
context = {}
|
|
663
|
+
context = {"use_new_ui": True}
|
|
677
664
|
if "export" in request.GET:
|
|
678
665
|
queryset = self.get_queryset()
|
|
679
666
|
model = queryset.model
|
|
@@ -895,14 +882,12 @@ class ObjectBulkDestroyViewMixin(NautobotViewSetMixin, BulkDestroyModelMixin):
|
|
|
895
882
|
return Response(data)
|
|
896
883
|
|
|
897
884
|
|
|
898
|
-
class ObjectBulkCreateViewMixin(NautobotViewSetMixin
|
|
885
|
+
class ObjectBulkCreateViewMixin(NautobotViewSetMixin):
|
|
899
886
|
"""
|
|
900
887
|
UI mixin to bulk create model instances.
|
|
901
888
|
"""
|
|
902
889
|
|
|
903
890
|
bulk_create_active_tab = "csv-data"
|
|
904
|
-
bulk_create_form_class = None
|
|
905
|
-
bulk_create_widget_attrs = {}
|
|
906
891
|
|
|
907
892
|
def _process_bulk_create_form(self, form):
|
|
908
893
|
# Iterate through CSV data and bind each row to a new model form instance.
|
|
@@ -918,18 +903,24 @@ class ObjectBulkCreateViewMixin(NautobotViewSetMixin, BulkCreateModelMixin):
|
|
|
918
903
|
self.bulk_create_active_tab = "csv-file"
|
|
919
904
|
else:
|
|
920
905
|
field_name = "csv_data"
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
906
|
+
|
|
907
|
+
csvtext = form.cleaned_data[field_name]
|
|
908
|
+
try:
|
|
909
|
+
data = NautobotCSVParser().parse(
|
|
910
|
+
stream=BytesIO(csvtext.encode("utf-8")),
|
|
911
|
+
parser_context={"request": request, "serializer_class": self.serializer_class},
|
|
912
|
+
)
|
|
913
|
+
serializer = self.serializer_class(data=data, context={"request": request}, many=True)
|
|
914
|
+
if serializer.is_valid():
|
|
915
|
+
new_objs = serializer.save()
|
|
929
916
|
else:
|
|
930
|
-
for
|
|
931
|
-
|
|
917
|
+
for row, errors in enumerate(serializer.errors, start=1):
|
|
918
|
+
for field, err in errors.items():
|
|
919
|
+
form.add_error(field_name, f"Row {row}: {field}: {err[0]}")
|
|
932
920
|
raise ValidationError("")
|
|
921
|
+
except exceptions.ParseError as exc:
|
|
922
|
+
form.add_error(None, str(exc))
|
|
923
|
+
raise ValidationError("")
|
|
933
924
|
|
|
934
925
|
# Enforce object-level permissions
|
|
935
926
|
if queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs):
|
nautobot/core/views/paginator.py
CHANGED