diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index 6578d569f8b8..9f02027f90cc 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -1,14 +1,14 @@ from _typeshed import Incomplete -from collections.abc import Callable +from collections.abc import Callable, Sequence from logging import Logger -from typing import Any, TypeVar +from typing import Any, Literal, TypeVar from typing_extensions import TypeAlias, deprecated from django.contrib import admin from django.contrib.admin.helpers import ActionForm from django.core.files import File from django.db.models import Model, QuerySet -from django.forms import Form, Media +from django.forms import Form from django.http.request import HttpRequest from django.http.response import HttpResponse from django.template.response import TemplateResponse @@ -38,33 +38,21 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): import_form_class: type[Form] = ... confirm_form_class: type[Form] = ... from_encoding: str + import_error_display: Sequence[Literal["message", "row", "traceback"]] skip_admin_log: bool | None tmp_storage_class: str | type[BaseStorage] def get_skip_admin_log(self) -> bool: ... def get_tmp_storage_class(self) -> type[BaseStorage]: ... + def get_tmp_storage_class_kwargs(self) -> dict[str, Any]: ... def has_import_permission(self, request: HttpRequest) -> bool: ... def get_urls(self) -> list[URLPattern]: ... - def process_import(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... - def process_dataset( - self, - dataset: Dataset, - confirm_form: Form, - request: HttpRequest, - *args: Any, - rollback_on_validation_errors: bool = False, - **kwargs: Any, - ) -> Result: ... + def process_import(self, request: HttpRequest, **kwargs: Any) -> HttpResponse: ... + def process_dataset(self, dataset: Dataset, form: Form, request: HttpRequest, **kwargs: Any) -> Result: ... def process_result(self, result: Result, request: HttpRequest) -> HttpResponse: ... def generate_log_entries(self, result: Result, request: HttpRequest) -> None: ... def add_success_message(self, result: Result, request: HttpRequest) -> None: ... def get_import_context_data(self, **kwargs: Any) -> dict[str, Any]: ... def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... - @deprecated("Use get_import_form_class instead") - def get_import_form(self) -> type[Form]: ... - @deprecated("Use get_confirm_form_class instead") - def get_confirm_import_form(self) -> type[Form]: ... - @deprecated("Use get_import_form_kwargs or get_confirm_form_kwargs") - def get_form_kwargs(self, form: Form, *args: Any, **kwargs: Any) -> dict[str, Any]: ... def create_import_form(self, request: HttpRequest) -> Form: ... def get_import_form_class(self, request: HttpRequest) -> type[Form]: ... def get_import_form_kwargs(self, request: HttpRequest) -> dict[str, Any]: ... @@ -73,42 +61,49 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): def get_confirm_form_class(self, request: HttpRequest) -> type[Form]: ... def get_confirm_form_kwargs(self, request: HttpRequest, import_form: Form | None = None) -> dict[str, Any]: ... def get_confirm_form_initial(self, request: HttpRequest, import_form: Form | None) -> dict[str, Any]: ... - def get_import_data_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def get_import_data_kwargs(self, **kwargs: Any) -> dict[str, Any]: ... def write_to_tmp_storage(self, import_file: File[bytes], input_format: Format) -> BaseStorage: ... def add_data_read_fail_error_to_form(self, form: Form, e: Exception) -> None: ... - def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... + def import_action(self, request: HttpRequest, **kwargs: Any) -> TemplateResponse: ... def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase): - import_export_change_list_template: str | None + import_export_change_list_template: str export_template_name: str to_encoding: str | None export_form_class: type[Form] = ... def get_urls(self) -> list[URLPattern]: ... def has_export_permission(self, request: HttpRequest) -> bool: ... def get_export_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ... - def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... + def get_export_data( + self, file_format: Format, request: HttpRequest, queryset: QuerySet[_ModelT], **kwargs: Any + ) -> str | bytes: ... def get_export_context_data(self, **kwargs: Any) -> dict[str, Any]: ... def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... - @deprecated("Use get_export_form_class or use the export_form_class attribute") - def get_export_form(self) -> Form: ... def get_export_form_class(self) -> type[Form]: ... - def export_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... + def export_action(self, request: HttpRequest) -> TemplateResponse: ... + @deprecated( + "The 'get_valid_export_item_pks()' method is deprecated and will be removed in a future release. " + "Overwrite 'get_queryset()' or 'get_export_queryset()' instead." + ) + def get_valid_export_item_pks(self, request: HttpRequest) -> list[str]: ... def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... # type: ignore[override] + def init_request_context_data(self, request: HttpRequest, form: Form) -> dict[str, Any]: ... -class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]): - import_export_change_list_template: str - +class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]): ... class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc] class ExportActionMixin(ExportMixin[_ModelT]): + change_form_template: str + show_change_form_export: bool action_form: type[ActionForm] - def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def change_view( + self, request: HttpRequest, object_id: str, form_url: str = "", extra_context: dict[str, Any] | None = None + ) -> HttpResponse: ... + def response_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponse: ... def export_admin_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> HttpResponse: ... def get_actions(self, request: HttpRequest) -> dict[str, tuple[Callable[..., str], str, str] | None]: ... - @property - def media(self) -> Media: ... class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc] class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... # type: ignore[misc] diff --git a/stubs/django-import-export/import_export/command_utils.pyi b/stubs/django-import-export/import_export/command_utils.pyi new file mode 100644 index 000000000000..a3c1fb5e4236 --- /dev/null +++ b/stubs/django-import-export/import_export/command_utils.pyi @@ -0,0 +1,12 @@ +from _typeshed import StrPath +from typing import Any + +from .formats.base_formats import Format +from .resources import ModelResource + +def get_resource_class(model_or_resource_class: str) -> ModelResource[Any]: ... + +MIME_TYPE_FORMAT_MAPPING: dict[str, type[Format]] + +def get_format_class(format_name: str, file_name: StrPath, encoding: str | None = None) -> Format: ... +def get_default_format_names() -> str: ... diff --git a/stubs/django-import-export/import_export/declarative.pyi b/stubs/django-import-export/import_export/declarative.pyi new file mode 100644 index 000000000000..e0c723239488 --- /dev/null +++ b/stubs/django-import-export/import_export/declarative.pyi @@ -0,0 +1,11 @@ +import _typeshed +from logging import Logger +from typing import Any + +logger: Logger + +class DeclarativeMetaclass(type): + def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ... + +class ModelDeclarativeMetaclass(DeclarativeMetaclass): + def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ... diff --git a/stubs/django-import-export/import_export/exceptions.pyi b/stubs/django-import-export/import_export/exceptions.pyi index f8a69dfbb6d4..3f6b99db52d9 100644 --- a/stubs/django-import-export/import_export/exceptions.pyi +++ b/stubs/django-import-export/import_export/exceptions.pyi @@ -1,2 +1,11 @@ +from typing import Any + class ImportExportError(Exception): ... class FieldError(ImportExportError): ... +class WidgetError(ImportExportError): ... + +class ImportError(ImportExportError): + error: Exception + number: int | None + row: dict[str, Any] | None + def __init__(self, error: Exception, number: int | None = None, row: dict[str, Any] | None = None) -> None: ... diff --git a/stubs/django-import-export/import_export/fields.pyi b/stubs/django-import-export/import_export/fields.pyi index 5520ffe6a517..d17fb1d7e2eb 100644 --- a/stubs/django-import-export/import_export/fields.pyi +++ b/stubs/django-import-export/import_export/fields.pyi @@ -27,8 +27,8 @@ class Field: dehydrate_method: str | None = None, m2m_add: bool = False, ) -> None: ... - def clean(self, data: Mapping[str, Any], **kwargs: Any) -> Any: ... - def get_value(self, obj: Model) -> Any: ... - def save(self, obj: Model, data: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... - def export(self, obj: Model) -> str: ... + def clean(self, row: Mapping[str, Any], **kwargs: Any) -> Any: ... + def get_value(self, instance: Model) -> Any: ... + def save(self, instance: Model, row: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... + def export(self, instance: Model, **kwargs: Any) -> str: ... def get_dehydrate_method(self, field_name: str | None = None) -> str: ... diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi index fdc77740b4e3..a2206683b830 100644 --- a/stubs/django-import-export/import_export/formats/base_formats.pyi +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -1,9 +1,12 @@ from _typeshed import Incomplete, ReadableBuffer +from logging import Logger from typing import IO, Any, ClassVar from typing_extensions import Self, TypeAlias Dataset: TypeAlias = Incomplete # tablib.Dataset +logger: Logger + class Format: def get_title(self) -> type[Self]: ... def create_dataset(self, in_stream: str | bytes | IO[Any]) -> Dataset: ... @@ -43,8 +46,7 @@ class TSV(TextFormat): class ODS(TextFormat): def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... -class HTML(TextFormat): - def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... +class HTML(TextFormat): ... class XLS(TablibFormat): def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... @@ -55,3 +57,4 @@ class XLSX(TablibFormat): def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... # type: ignore[override] DEFAULT_FORMATS: list[type[Format]] +BINARY_FORMATS: list[type[Format]] diff --git a/stubs/django-import-export/import_export/forms.pyi b/stubs/django-import-export/import_export/forms.pyi index 333ad58e896f..910554c49bbb 100644 --- a/stubs/django-import-export/import_export/forms.pyi +++ b/stubs/django-import-export/import_export/forms.pyi @@ -1,31 +1,38 @@ +from collections.abc import Iterable, Sequence from typing import Any from django import forms -from django.contrib.admin.helpers import ActionForm from .formats.base_formats import Format -from .resources import Resource +from .resources import ModelResource, Resource class ImportExportFormBase(forms.Form): resource: forms.ChoiceField - def __init__(self, *args: Any, resources: list[type[Resource[Any]]] | None = None, **kwargs: Any) -> None: ... + format: forms.ChoiceField + def __init__( + self, formats: list[type[Format]], resources: list[type[Resource[Any]]] | None = None, **kwargs: Any + ) -> None: ... class ImportForm(ImportExportFormBase): import_file: forms.FileField - input_format: forms.ChoiceField - def __init__(self, import_formats: list[Format], *args: Any, **kwargs: Any) -> None: ... + field_order: Sequence[str] @property def media(self) -> forms.Media: ... class ConfirmImportForm(forms.Form): import_file_name: forms.CharField original_file_name: forms.CharField - input_format: forms.CharField resource: forms.CharField def clean_import_file_name(self) -> str: ... class ExportForm(ImportExportFormBase): - file_format: forms.ChoiceField - def __init__(self, formats: list[Format], *args: Any, **kwargs: Any) -> None: ... + export_items: forms.MultipleChoiceField -def export_action_form_factory(formats: list[tuple[str, str]]) -> type[ActionForm]: ... +class SelectableFieldsExportForm(ExportForm): + resources: Iterable[ModelResource[Any]] + is_selectable_fields_form: bool + resource_fields: dict[str, list[str]] + @staticmethod + def create_boolean_field_name(resource: ModelResource[Any], field_name: str) -> str: ... + def get_selected_resource(self) -> ModelResource[Any]: ... + def get_selected_resource_export_fields(self) -> list[str]: ... diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index ac60a5a7ffef..947753288e53 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -1,7 +1,7 @@ from _typeshed import Incomplete, SupportsGetItem from logging import Logger from typing import Any, Generic, TypeVar -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated from django.db.models import Model, QuerySet from django.forms import BaseForm, Form @@ -19,7 +19,6 @@ logger: Logger _ModelT = TypeVar("_ModelT", bound=Model) class BaseImportExportMixin(Generic[_ModelT]): - resource_class: type[Resource[_ModelT]] resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]] @property def formats(self) -> list[type[Format]]: ... @@ -28,39 +27,39 @@ class BaseImportExportMixin(Generic[_ModelT]): @property def import_formats(self) -> list[type[Format]]: ... def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]) -> None: ... - def get_resource_classes(self) -> list[type[Resource[_ModelT]]]: ... + def get_resource_classes(self, request: HttpRequest) -> list[type[Resource[_ModelT]]]: ... def get_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... def get_resource_index(self, form: Form) -> int: ... class BaseImportMixin(BaseImportExportMixin[_ModelT]): - def get_import_resource_classes(self) -> list[type[Resource[_ModelT]]]: ... + skip_import_confirm: bool + def get_import_resource_classes(self, request: HttpRequest) -> list[type[Resource[_ModelT]]]: ... def get_import_formats(self) -> list[Format]: ... - def get_import_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... - def choose_import_resource_class(self, form: Form) -> type[Resource[_ModelT]]: ... + def get_import_resource_kwargs(self, request: HttpRequest, **kwargs: Any) -> dict[str, Any]: ... + def choose_import_resource_class(self, form: Form, request: HttpRequest) -> type[Resource[_ModelT]]: ... + def is_skip_import_confirm_enabled(self) -> bool: ... class BaseExportMixin(BaseImportExportMixin[_ModelT]): model: Model - escape_exported_data: bool - escape_html: bool - escape_formulae: bool - @property - def should_escape_html(self) -> bool: ... - @property - def should_escape_formulae(self) -> bool: ... + skip_export_form: bool + skip_export_form_from_action: bool def get_export_formats(self) -> list[Format]: ... - def get_export_resource_classes(self) -> list[Resource[_ModelT]]: ... - def choose_export_resource_class(self, form: Form) -> Resource[_ModelT]: ... - def get_export_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... - def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> Dataset: ... + def get_export_resource_classes(self, request: HttpRequest) -> list[Resource[_ModelT]]: ... + def choose_export_resource_class(self, form: Form, request: HttpRequest) -> Resource[_ModelT]: ... + def get_export_resource_kwargs(self, request: HttpRequest, **kwargs: Any) -> dict[str, Any]: ... + def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], **kwargs: Any) -> Dataset: ... def get_export_filename(self, file_format: Format) -> str: ... + def is_skip_export_form_enabled(self) -> bool: ... + def is_skip_export_form_from_action_enabled(self) -> bool: ... class ExportViewMixin(BaseExportMixin[_ModelT]): form_class: type[BaseForm] = ... - def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... + def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], **kwargs: Any) -> str | bytes: ... def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... def get_form_kwargs(self) -> dict[str, Any]: ... _FormT = TypeVar("_FormT", bound=BaseForm) +@deprecated("ExportViewFormMixin is deprecated and will be removed in a future release.") class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): # type: ignore[misc] def form_valid(self, form: _FormT) -> HttpResponse: ... diff --git a/stubs/django-import-export/import_export/options.pyi b/stubs/django-import-export/import_export/options.pyi new file mode 100644 index 000000000000..6ddc523acc85 --- /dev/null +++ b/stubs/django-import-export/import_export/options.pyi @@ -0,0 +1,32 @@ +from collections.abc import Sequence +from typing import Any, Generic, TypeVar + +from django.db.models import Model + +from .instance_loaders import BaseInstanceLoader + +_ModelT = TypeVar("_ModelT", bound=Model) + +class ResourceOptions(Generic[_ModelT]): + model: _ModelT | str + fields: Sequence[str] | None + exclude: Sequence[str] | None + instance_loader_class: type[BaseInstanceLoader] | None + import_id_fields: Sequence[str] + export_order: Sequence[str] | None + import_order: Sequence[str] | None + widgets: dict[str, Any] | None + use_transactions: bool | None + skip_unchanged: bool + report_skipped: bool + clean_model_instances: bool + chunk_size: int | None + skip_diff: bool + skip_html_diff: bool + use_bulk: bool + batch_size: int + force_init_instance: bool + using_db: str | None + store_row_values: bool + store_instance: bool + use_natural_foreign_keys: bool diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi index 895b430ccf9d..1c5a5e7f1b24 100644 --- a/stubs/django-import-export/import_export/resources.pyi +++ b/stubs/django-import-export/import_export/resources.pyi @@ -6,49 +6,21 @@ from logging import Logger from typing import Any, ClassVar, Generic, Literal, NoReturn, TypeVar, overload from typing_extensions import TypeAlias, deprecated -from django.db.models import Field as DjangoField, ForeignObjectRel, Model, QuerySet +from django.db.models import Field as DjangoField, Model, QuerySet from django.utils.safestring import SafeString +from .declarative import DeclarativeMetaclass, ModelDeclarativeMetaclass from .fields import Field from .instance_loaders import BaseInstanceLoader +from .options import ResourceOptions from .results import Error, Result, RowResult from .widgets import ForeignKeyWidget, ManyToManyWidget, Widget Dataset: TypeAlias = _typeshed.Incomplete # tablib.Dataset logger: Logger -@overload -def get_related_model(field: ForeignObjectRel) -> Model: ... -@overload -def get_related_model(field: DjangoField[Any, Any]) -> Model | None: ... def has_natural_foreign_key(model: Model) -> bool: ... -class ResourceOptions(Generic[_ModelT]): - model: _ModelT - fields: Sequence[str] | None - exclude: Sequence[str] | None - instance_loader_class: type[BaseInstanceLoader] | None - import_id_fields: Sequence[str] - export_order: Sequence[str] | None - widgets: dict[str, Any] | None - use_transactions: bool | None - skip_unchanged: bool - report_skipped: bool - clean_model_instances: bool - chunk_size: int | None - skip_diff: bool - skip_html_diff: bool - use_bulk: bool - batch_size: int - force_init_instance: bool - using_db: str | None - store_row_values: bool - store_instance: bool - use_natural_foreign_keys: bool - -class DeclarativeMetaclass(type): - def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ... - class Diff: left: list[str] right: list[str] @@ -78,6 +50,7 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass): def get_db_connection_name(self) -> str: ... def get_use_transactions(self) -> bool: ... def get_chunk_size(self) -> int: ... + @deprecated("The 'get_fields()' method is deprecated and will be removed in a future release.") def get_fields(self, **kwargs: Any) -> list[Field]: ... def get_field_name(self, field: Field) -> str: ... def init_instance(self, row: dict[str, Any] | None = None) -> _ModelT: ... @@ -105,56 +78,35 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass): def validate_instance( self, instance: _ModelT, import_validation_errors: dict[str, Any] | None = None, validate_unique: bool = True ) -> None: ... - def save_instance( - self, instance: _ModelT, is_create: bool, using_transactions: bool = True, dry_run: bool = False - ) -> None: ... - def before_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ... - def after_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ... - def delete_instance(self, instance: _ModelT, using_transactions: bool = True, dry_run: bool = False) -> None: ... - def before_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ... - def after_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ... - def import_field(self, field: Field, obj: _ModelT, data: dict[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... + # For all the definitions below (from `save_instance()` to `import_row()`), `**kwargs` should contain: + # dry_run: bool, use_transactions: bool, row_number: int, retain_instance_in_row_result: bool. + # Users are free to pass extra arguments in `import_data()`so PEP 728 can probably be leveraged here. + def save_instance(self, instance: _ModelT, is_create: bool, row: dict[str, Any], **kwargs: Any) -> None: ... + def do_instance_save(self, instance: _ModelT) -> None: ... + def before_save_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def after_save_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def before_delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def after_delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def import_field(self, field: Field, instance: _ModelT, row: dict[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... def get_import_fields(self) -> list[Field]: ... - def import_obj(self, obj: _ModelT, data: dict[str, Any], dry_run: bool, **kwargs: Any) -> None: ... - def save_m2m(self, obj: _ModelT, data: dict[str, Any], using_transactions: bool, dry_run: bool) -> None: ... + def import_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... + def save_m2m(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ... def for_delete(self, row: dict[str, Any], instance: _ModelT) -> bool: ... def skip_row( self, instance: _ModelT, original: _ModelT, row: dict[str, Any], import_validation_errors: dict[str, Any] | None = None ) -> bool: ... def get_diff_headers(self) -> list[str]: ... - def before_import(self, dataset: Dataset, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... - def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... - def before_import_row(self, row: dict[str, Any], row_number: int | None = None, **kwargs: Any) -> None: ... - def after_import_row( - self, row: dict[str, Any], row_result: RowResult, row_number: int | None = None, **kwargs: Any - ) -> None: ... - def after_import_instance(self, instance: _ModelT, new: bool, row_number: int | None = None, **kwargs: Any) -> None: ... + def before_import(self, dataset: Dataset, **kwargs: Any) -> None: ... + def after_import(self, dataset: Dataset, result: Result, **kwargs: Any) -> None: ... + def before_import_row(self, row: dict[str, Any], **kwargs: Any) -> None: ... + def after_import_row(self, row: dict[str, Any], row_result: RowResult, **kwargs: Any) -> None: ... + def after_init_instance(self, instance: _ModelT, new: bool, row: dict[str, Any], **kwargs: Any) -> None: ... @overload def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[True]) -> NoReturn: ... @overload def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[False] = ...) -> None: ... - @overload - @deprecated("raise_errors argument is deprecated and will be removed in a future release.") - def import_row( - self, - row: dict[str, Any], - instance_loader: BaseInstanceLoader, - using_transactions: bool = True, - dry_run: bool = False, - *, - raise_errors: bool, - **kwargs: Any, - ) -> RowResult: ... - @overload - def import_row( - self, - row: dict[str, Any], - instance_loader: BaseInstanceLoader, - using_transactions: bool = True, - dry_run: bool = False, - raise_errors: None = None, - **kwargs: Any, - ) -> RowResult: ... + def import_row(self, row: dict[str, Any], instance_loader: BaseInstanceLoader, **kwargs: Any) -> RowResult: ... def import_data( self, dataset: Dataset, @@ -165,8 +117,6 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass): rollback_on_validation_errors: bool = False, **kwargs: Any, ) -> Result: ... - @overload - @deprecated("rollback_on_validation_errors argument is deprecated and will be removed in a future release.") def import_data_inner( self, dataset: Dataset, @@ -174,35 +124,21 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass): raise_errors: bool, using_transactions: bool, collect_failed_rows: bool, - rollback_on_validation_errors: bool, - **kwargs: Any, - ) -> Result: ... - @overload - def import_data_inner( - self, - dataset: Dataset, - dry_run: bool, - raise_errors: bool, - using_transactions: bool, - collect_failed_rows: bool, - rollback_on_validation_errors: None = None, **kwargs: Any, ) -> Result: ... + def get_import_order(self) -> tuple[str, ...]: ... def get_export_order(self) -> tuple[str, ...]: ... - def before_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> None: ... - def after_export(self, queryset: QuerySet[_ModelT], data: Dataset, *args: Any, **kwargs: Any) -> None: ... - def filter_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ... - def export_field(self, field: Field, obj: _ModelT) -> str: ... - def get_export_fields(self) -> list[Field]: ... - def export_resource(self, obj: _ModelT) -> list[str]: ... - def get_export_headers(self) -> list[str]: ... + def before_export(self, queryset: QuerySet[_ModelT], **kwargs: Any) -> None: ... + def after_export(self, queryset: QuerySet[_ModelT], dataset: Dataset, **kwargs: Any) -> None: ... + def filter_export(self, queryset: QuerySet[_ModelT], **kwargs: Any) -> QuerySet[_ModelT]: ... + def export_field(self, field: Field, instance: _ModelT, **kwargs: Any) -> str: ... + def get_export_fields(self, selected_fields: Sequence[str] | None = None) -> list[Field]: ... + def export_resource(self, instance: _ModelT, selected_fields: Sequence[str] | None = None, **kwargs: Any) -> list[str]: ... + def get_export_headers(self, selected_fields: Sequence[str] | None = None) -> list[str]: ... def get_user_visible_headers(self) -> list[str]: ... def get_user_visible_fields(self) -> list[str]: ... def iter_queryset(self, queryset: QuerySet[_ModelT]) -> Iterator[_ModelT]: ... - def export(self, *args: Any, queryset: QuerySet[_ModelT] | None = None, **kwargs: Any) -> Dataset: ... - -class ModelDeclarativeMetaclass(DeclarativeMetaclass): - def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ... + def export(self, queryset: QuerySet[_ModelT] | None = None, **kwargs: Any) -> Dataset: ... class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass): DEFAULT_RESOURCE_FIELD: ClassVar[type[Field]] = ... @@ -214,12 +150,12 @@ class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass): @classmethod def widget_from_django_field(cls, f: DjangoField[Any, Any], default: type[Widget] = ...) -> type[Widget]: ... @classmethod - def widget_kwargs_for_field(self, field_name: str) -> dict[str, Any]: ... + def widget_kwargs_for_field(cls, field_name: str, django_field: DjangoField[Any, Any]) -> dict[str, Any]: ... @classmethod def field_from_django_field(cls, field_name: str, django_field: DjangoField[Any, Any], readonly: bool) -> Field: ... def get_queryset(self) -> QuerySet[_ModelT]: ... def init_instance(self, row: dict[str, Any] | None = None) -> _ModelT: ... - def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... + def after_import(self, dataset: Dataset, result: Result, **kwargs: Any) -> None: ... @classmethod def get_display_name(cls) -> str: ... diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi index dd50fd7e7e39..509d94436403 100644 --- a/stubs/django-import-export/import_export/results.pyi +++ b/stubs/django-import-export/import_export/results.pyi @@ -1,6 +1,7 @@ from _typeshed import Incomplete from collections import OrderedDict from collections.abc import Iterator +from functools import cached_property from typing import Any, ClassVar, Literal from typing_extensions import TypeAlias @@ -11,9 +12,11 @@ Dataset: TypeAlias = Incomplete # tablib.Dataset class Error: error: Exception - traceback: str row: dict[str, Any] - def __init__(self, error: Exception, traceback: str | None = None, row: dict[str, Any] | None = None) -> None: ... + number: int | None + def __init__(self, error: Exception, row: dict[str, Any] | None = None, number: int | None = None) -> None: ... + @cached_property + def traceback(self) -> str: ... _ImportType: TypeAlias = Literal["update", "new", "delete", "skip", "error", "invalid"] @@ -34,9 +37,20 @@ class RowResult: object_repr: str | None instance: Model original: Model - new_record: bool | None def __init__(self) -> None: ... def add_instance_info(self, instance: Model) -> None: ... + def is_update(self) -> bool: ... + def is_new(self) -> bool: ... + def is_delete(self) -> bool: ... + def is_skip(self) -> bool: ... + def is_error(self) -> bool: ... + def is_invalid(self) -> bool: ... + def is_valid(self) -> bool: ... + +class ErrorRow: + number: int + errors: list[Error] + def __init__(self, number: int, errors: list[Error]) -> None: ... class InvalidRow: number: int @@ -56,6 +70,7 @@ class Result: diff_headers: list[str] rows: list[RowResult] invalid_rows: list[InvalidRow] + error_rows: list[ErrorRow] failed_dataset: Dataset totals: OrderedDict[_ImportType, int] total_rows: int @@ -66,6 +81,7 @@ class Result: def add_dataset_headers(self, headers: list[str] | None) -> None: ... def append_failed_row(self, row: dict[str, Any], error: Exception) -> None: ... def append_invalid_row(self, number: int, row: dict[str, Any], validation_error: ValidationError) -> None: ... + def append_error_row(self, number: int, row: dict[str, Any], errors: list[Error]) -> None: ... def increment_row_result_total(self, row_result: RowResult) -> None: ... def row_errors(self) -> list[tuple[int, Any]]: ... def has_errors(self) -> bool: ... diff --git a/stubs/django-import-export/import_export/utils.pyi b/stubs/django-import-export/import_export/utils.pyi index 49985a35ada5..d4b968a3f3f7 100644 --- a/stubs/django-import-export/import_export/utils.pyi +++ b/stubs/django-import-export/import_export/utils.pyi @@ -1,11 +1,9 @@ -from collections.abc import Callable from types import TracebackType -from typing import Any, TypeVar +from typing import Any, overload +from django.db.models import Field as DjangoField, ForeignObjectRel, Model from django.db.transaction import Atomic -_C = TypeVar("_C", bound=Callable[..., Any]) - class atomic_if_using_transaction: using_transactions: bool context_manager: Atomic @@ -15,4 +13,7 @@ class atomic_if_using_transaction: self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None ) -> None: ... -def original(method: _C) -> _C: ... +@overload +def get_related_model(field: ForeignObjectRel) -> Model: ... +@overload +def get_related_model(field: DjangoField[Any, Any]) -> Model | None: ... diff --git a/stubs/django-import-export/import_export/widgets.pyi b/stubs/django-import-export/import_export/widgets.pyi index e91d2a424124..16296912b39b 100644 --- a/stubs/django-import-export/import_export/widgets.pyi +++ b/stubs/django-import-export/import_export/widgets.pyi @@ -1,52 +1,56 @@ from collections.abc import Mapping from datetime import datetime -from typing import Any, ClassVar, Generic, TypeVar +from typing import Any, ClassVar, Generic, TypeVar, overload +from typing_extensions import deprecated from django.db.models import Model, QuerySet def format_datetime(value: datetime, datetime_format: str) -> str: ... class Widget: + coerce_to_string: bool + def __init__(self, coerce_to_string: bool = True) -> None: ... def clean(self, value: Any, row: Mapping[str, Any] | None = None, **kwargs: Any) -> Any: ... - def render(self, value: Any, obj: Model | None = None) -> Any: ... + @overload + @deprecated("The 'obj' parameter is deprecated and will be removed in a future release.") + def render(self, value: Any, obj: Model, **kwargs: Any) -> Any: ... + @overload + def render(self, value: Any, obj: None = None, **kwargs: Any) -> Any: ... class NumberWidget(Widget): - coerce_to_string: bool - def __init__(self, coerce_to_string: bool = False) -> None: ... def is_empty(self, value: Any) -> bool: ... - def render(self, value: Any, obj: Model | None = None) -> Any: ... class FloatWidget(NumberWidget): ... class IntegerWidget(NumberWidget): ... class DecimalWidget(NumberWidget): ... class CharWidget(Widget): - coerce_to_string: bool allow_blank: bool - def __init__(self, coerce_to_string: bool = False, allow_blank: bool = False) -> None: ... + def __init__(self, coerce_to_string: bool = True, allow_blank: bool = True) -> None: ... class BooleanWidget(Widget): TRUE_VALUES: ClassVar[list[str | int | bool]] FALSE_VALUES: ClassVar[list[str | int | bool]] NULL_VALUES: ClassVar[list[str | None]] + def __init__(self, coerce_to_string: bool = True) -> None: ... class DateWidget(Widget): formats: tuple[str, ...] - def __init__(self, format: str | None = None) -> None: ... + def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ... class DateTimeWidget(Widget): formats: tuple[str, ...] - def __init__(self, format: str | None = None) -> None: ... + def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ... class TimeWidget(Widget): formats: tuple[str, ...] - def __init__(self, format: str | None = None) -> None: ... + def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ... class DurationWidget(Widget): ... class SimpleArrayWidget(Widget): separator: str - def __init__(self, separator: str | None = None) -> None: ... + def __init__(self, separator: str | None = None, coerce_to_string: bool = True) -> None: ... class JSONWidget(Widget): ... @@ -55,9 +59,13 @@ _ModelT = TypeVar("_ModelT", bound=Model) class ForeignKeyWidget(Widget, Generic[_ModelT]): model: _ModelT field: str + key_is_id: bool use_natural_foreign_keys: bool - def __init__(self, model: _ModelT, field: str = "pk", use_natural_foreign_keys: bool = False, **kwargs: Any) -> None: ... + def __init__( + self, model: _ModelT, field: str = "pk", use_natural_foreign_keys: bool = False, key_is_id: bool = False, **kwargs: Any + ) -> None: ... def get_queryset(self, value: Any, row: Mapping[str, Any], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ... + def get_lookup_kwargs(self, value: Any, row: Mapping[str, Any] | None = None, **kwargs: Any) -> dict[str, Any]: ... class ManyToManyWidget(Widget, Generic[_ModelT]): model: _ModelT diff --git a/stubs/django-import-export/management/__init__.pyi b/stubs/django-import-export/management/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/django-import-export/management/commands/__init__.pyi b/stubs/django-import-export/management/commands/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/django-import-export/management/commands/export.pyi b/stubs/django-import-export/management/commands/export.pyi new file mode 100644 index 000000000000..8d75fb4d613f --- /dev/null +++ b/stubs/django-import-export/management/commands/export.pyi @@ -0,0 +1,3 @@ +from django.core.management.base import BaseCommand + +class Command(BaseCommand): ... diff --git a/stubs/django-import-export/management/commands/import.pyi b/stubs/django-import-export/management/commands/import.pyi new file mode 100644 index 000000000000..8d75fb4d613f --- /dev/null +++ b/stubs/django-import-export/management/commands/import.pyi @@ -0,0 +1,3 @@ +from django.core.management.base import BaseCommand + +class Command(BaseCommand): ...