Skip to content

Commit e26219d

Browse files
authoredFeb 3, 2025··
Bump django-import-export to 4.3.4 (#13414)
1 parent 983fc03 commit e26219d

17 files changed

+218
-183
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from _typeshed import Incomplete
2-
from collections.abc import Callable
2+
from collections.abc import Callable, Sequence
33
from logging import Logger
4-
from typing import Any, TypeVar
4+
from typing import Any, Literal, TypeVar
55
from typing_extensions import TypeAlias, deprecated
66

77
from django.contrib import admin
88
from django.contrib.admin.helpers import ActionForm
99
from django.core.files import File
1010
from django.db.models import Model, QuerySet
11-
from django.forms import Form, Media
11+
from django.forms import Form
1212
from django.http.request import HttpRequest
1313
from django.http.response import HttpResponse
1414
from django.template.response import TemplateResponse
@@ -38,33 +38,21 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase):
3838
import_form_class: type[Form] = ...
3939
confirm_form_class: type[Form] = ...
4040
from_encoding: str
41+
import_error_display: Sequence[Literal["message", "row", "traceback"]]
4142
skip_admin_log: bool | None
4243
tmp_storage_class: str | type[BaseStorage]
4344
def get_skip_admin_log(self) -> bool: ...
4445
def get_tmp_storage_class(self) -> type[BaseStorage]: ...
46+
def get_tmp_storage_class_kwargs(self) -> dict[str, Any]: ...
4547
def has_import_permission(self, request: HttpRequest) -> bool: ...
4648
def get_urls(self) -> list[URLPattern]: ...
47-
def process_import(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
48-
def process_dataset(
49-
self,
50-
dataset: Dataset,
51-
confirm_form: Form,
52-
request: HttpRequest,
53-
*args: Any,
54-
rollback_on_validation_errors: bool = False,
55-
**kwargs: Any,
56-
) -> Result: ...
49+
def process_import(self, request: HttpRequest, **kwargs: Any) -> HttpResponse: ...
50+
def process_dataset(self, dataset: Dataset, form: Form, request: HttpRequest, **kwargs: Any) -> Result: ...
5751
def process_result(self, result: Result, request: HttpRequest) -> HttpResponse: ...
5852
def generate_log_entries(self, result: Result, request: HttpRequest) -> None: ...
5953
def add_success_message(self, result: Result, request: HttpRequest) -> None: ...
6054
def get_import_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
6155
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
62-
@deprecated("Use get_import_form_class instead")
63-
def get_import_form(self) -> type[Form]: ...
64-
@deprecated("Use get_confirm_form_class instead")
65-
def get_confirm_import_form(self) -> type[Form]: ...
66-
@deprecated("Use get_import_form_kwargs or get_confirm_form_kwargs")
67-
def get_form_kwargs(self, form: Form, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
6856
def create_import_form(self, request: HttpRequest) -> Form: ...
6957
def get_import_form_class(self, request: HttpRequest) -> type[Form]: ...
7058
def get_import_form_kwargs(self, request: HttpRequest) -> dict[str, Any]: ...
@@ -73,42 +61,49 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase):
7361
def get_confirm_form_class(self, request: HttpRequest) -> type[Form]: ...
7462
def get_confirm_form_kwargs(self, request: HttpRequest, import_form: Form | None = None) -> dict[str, Any]: ...
7563
def get_confirm_form_initial(self, request: HttpRequest, import_form: Form | None) -> dict[str, Any]: ...
76-
def get_import_data_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
64+
def get_import_data_kwargs(self, **kwargs: Any) -> dict[str, Any]: ...
7765
def write_to_tmp_storage(self, import_file: File[bytes], input_format: Format) -> BaseStorage: ...
7866
def add_data_read_fail_error_to_form(self, form: Form, e: Exception) -> None: ...
79-
def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ...
67+
def import_action(self, request: HttpRequest, **kwargs: Any) -> TemplateResponse: ...
8068
def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ...
8169

8270
class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase):
83-
import_export_change_list_template: str | None
71+
import_export_change_list_template: str
8472
export_template_name: str
8573
to_encoding: str | None
8674
export_form_class: type[Form] = ...
8775
def get_urls(self) -> list[URLPattern]: ...
8876
def has_export_permission(self, request: HttpRequest) -> bool: ...
8977
def get_export_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ...
90-
def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ...
78+
def get_export_data(
79+
self, file_format: Format, request: HttpRequest, queryset: QuerySet[_ModelT], **kwargs: Any
80+
) -> str | bytes: ...
9181
def get_export_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
9282
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
93-
@deprecated("Use get_export_form_class or use the export_form_class attribute")
94-
def get_export_form(self) -> Form: ...
9583
def get_export_form_class(self) -> type[Form]: ...
96-
def export_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ...
84+
def export_action(self, request: HttpRequest) -> TemplateResponse: ...
85+
@deprecated(
86+
"The 'get_valid_export_item_pks()' method is deprecated and will be removed in a future release. "
87+
"Overwrite 'get_queryset()' or 'get_export_queryset()' instead."
88+
)
89+
def get_valid_export_item_pks(self, request: HttpRequest) -> list[str]: ...
9790
def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ...
9891
def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... # type: ignore[override]
92+
def init_request_context_data(self, request: HttpRequest, form: Form) -> dict[str, Any]: ...
9993

100-
class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]):
101-
import_export_change_list_template: str
102-
94+
class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]): ...
10395
class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc]
10496

10597
class ExportActionMixin(ExportMixin[_ModelT]):
98+
change_form_template: str
99+
show_change_form_export: bool
106100
action_form: type[ActionForm]
107-
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
101+
def change_view(
102+
self, request: HttpRequest, object_id: str, form_url: str = "", extra_context: dict[str, Any] | None = None
103+
) -> HttpResponse: ...
104+
def response_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponse: ...
108105
def export_admin_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> HttpResponse: ...
109106
def get_actions(self, request: HttpRequest) -> dict[str, tuple[Callable[..., str], str, str] | None]: ...
110-
@property
111-
def media(self) -> Media: ...
112107

113108
class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc]
114109
class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... # type: ignore[misc]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from _typeshed import StrPath
2+
from typing import Any
3+
4+
from .formats.base_formats import Format
5+
from .resources import ModelResource
6+
7+
def get_resource_class(model_or_resource_class: str) -> ModelResource[Any]: ...
8+
9+
MIME_TYPE_FORMAT_MAPPING: dict[str, type[Format]]
10+
11+
def get_format_class(format_name: str, file_name: StrPath, encoding: str | None = None) -> Format: ...
12+
def get_default_format_names() -> str: ...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import _typeshed
2+
from logging import Logger
3+
from typing import Any
4+
5+
logger: Logger
6+
7+
class DeclarativeMetaclass(type):
8+
def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ...
9+
10+
class ModelDeclarativeMetaclass(DeclarativeMetaclass):
11+
def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ...
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1+
from typing import Any
2+
13
class ImportExportError(Exception): ...
24
class FieldError(ImportExportError): ...
5+
class WidgetError(ImportExportError): ...
6+
7+
class ImportError(ImportExportError):
8+
error: Exception
9+
number: int | None
10+
row: dict[str, Any] | None
11+
def __init__(self, error: Exception, number: int | None = None, row: dict[str, Any] | None = None) -> None: ...

‎stubs/django-import-export/import_export/fields.pyi

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class Field:
2727
dehydrate_method: str | None = None,
2828
m2m_add: bool = False,
2929
) -> None: ...
30-
def clean(self, data: Mapping[str, Any], **kwargs: Any) -> Any: ...
31-
def get_value(self, obj: Model) -> Any: ...
32-
def save(self, obj: Model, data: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ...
33-
def export(self, obj: Model) -> str: ...
30+
def clean(self, row: Mapping[str, Any], **kwargs: Any) -> Any: ...
31+
def get_value(self, instance: Model) -> Any: ...
32+
def save(self, instance: Model, row: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ...
33+
def export(self, instance: Model, **kwargs: Any) -> str: ...
3434
def get_dehydrate_method(self, field_name: str | None = None) -> str: ...

‎stubs/django-import-export/import_export/formats/base_formats.pyi

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from _typeshed import Incomplete, ReadableBuffer
2+
from logging import Logger
23
from typing import IO, Any, ClassVar
34
from typing_extensions import Self, TypeAlias
45

56
Dataset: TypeAlias = Incomplete # tablib.Dataset
67

8+
logger: Logger
9+
710
class Format:
811
def get_title(self) -> type[Self]: ...
912
def create_dataset(self, in_stream: str | bytes | IO[Any]) -> Dataset: ...
@@ -43,8 +46,7 @@ class TSV(TextFormat):
4346
class ODS(TextFormat):
4447
def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ...
4548

46-
class HTML(TextFormat):
47-
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
49+
class HTML(TextFormat): ...
4850

4951
class XLS(TablibFormat):
5052
def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ...
@@ -55,3 +57,4 @@ class XLSX(TablibFormat):
5557
def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... # type: ignore[override]
5658

5759
DEFAULT_FORMATS: list[type[Format]]
60+
BINARY_FORMATS: list[type[Format]]
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
1+
from collections.abc import Iterable, Sequence
12
from typing import Any
23

34
from django import forms
4-
from django.contrib.admin.helpers import ActionForm
55

66
from .formats.base_formats import Format
7-
from .resources import Resource
7+
from .resources import ModelResource, Resource
88

99
class ImportExportFormBase(forms.Form):
1010
resource: forms.ChoiceField
11-
def __init__(self, *args: Any, resources: list[type[Resource[Any]]] | None = None, **kwargs: Any) -> None: ...
11+
format: forms.ChoiceField
12+
def __init__(
13+
self, formats: list[type[Format]], resources: list[type[Resource[Any]]] | None = None, **kwargs: Any
14+
) -> None: ...
1215

1316
class ImportForm(ImportExportFormBase):
1417
import_file: forms.FileField
15-
input_format: forms.ChoiceField
16-
def __init__(self, import_formats: list[Format], *args: Any, **kwargs: Any) -> None: ...
18+
field_order: Sequence[str]
1719
@property
1820
def media(self) -> forms.Media: ...
1921

2022
class ConfirmImportForm(forms.Form):
2123
import_file_name: forms.CharField
2224
original_file_name: forms.CharField
23-
input_format: forms.CharField
2425
resource: forms.CharField
2526
def clean_import_file_name(self) -> str: ...
2627

2728
class ExportForm(ImportExportFormBase):
28-
file_format: forms.ChoiceField
29-
def __init__(self, formats: list[Format], *args: Any, **kwargs: Any) -> None: ...
29+
export_items: forms.MultipleChoiceField
3030

31-
def export_action_form_factory(formats: list[tuple[str, str]]) -> type[ActionForm]: ...
31+
class SelectableFieldsExportForm(ExportForm):
32+
resources: Iterable[ModelResource[Any]]
33+
is_selectable_fields_form: bool
34+
resource_fields: dict[str, list[str]]
35+
@staticmethod
36+
def create_boolean_field_name(resource: ModelResource[Any], field_name: str) -> str: ...
37+
def get_selected_resource(self) -> ModelResource[Any]: ...
38+
def get_selected_resource_export_fields(self) -> list[str]: ...
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from _typeshed import Incomplete, SupportsGetItem
22
from logging import Logger
33
from typing import Any, Generic, TypeVar
4-
from typing_extensions import TypeAlias
4+
from typing_extensions import TypeAlias, deprecated
55

66
from django.db.models import Model, QuerySet
77
from django.forms import BaseForm, Form
@@ -19,7 +19,6 @@ logger: Logger
1919
_ModelT = TypeVar("_ModelT", bound=Model)
2020

2121
class BaseImportExportMixin(Generic[_ModelT]):
22-
resource_class: type[Resource[_ModelT]]
2322
resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]
2423
@property
2524
def formats(self) -> list[type[Format]]: ...
@@ -28,39 +27,39 @@ class BaseImportExportMixin(Generic[_ModelT]):
2827
@property
2928
def import_formats(self) -> list[type[Format]]: ...
3029
def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]) -> None: ...
31-
def get_resource_classes(self) -> list[type[Resource[_ModelT]]]: ...
30+
def get_resource_classes(self, request: HttpRequest) -> list[type[Resource[_ModelT]]]: ...
3231
def get_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
3332
def get_resource_index(self, form: Form) -> int: ...
3433

3534
class BaseImportMixin(BaseImportExportMixin[_ModelT]):
36-
def get_import_resource_classes(self) -> list[type[Resource[_ModelT]]]: ...
35+
skip_import_confirm: bool
36+
def get_import_resource_classes(self, request: HttpRequest) -> list[type[Resource[_ModelT]]]: ...
3737
def get_import_formats(self) -> list[Format]: ...
38-
def get_import_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
39-
def choose_import_resource_class(self, form: Form) -> type[Resource[_ModelT]]: ...
38+
def get_import_resource_kwargs(self, request: HttpRequest, **kwargs: Any) -> dict[str, Any]: ...
39+
def choose_import_resource_class(self, form: Form, request: HttpRequest) -> type[Resource[_ModelT]]: ...
40+
def is_skip_import_confirm_enabled(self) -> bool: ...
4041

4142
class BaseExportMixin(BaseImportExportMixin[_ModelT]):
4243
model: Model
43-
escape_exported_data: bool
44-
escape_html: bool
45-
escape_formulae: bool
46-
@property
47-
def should_escape_html(self) -> bool: ...
48-
@property
49-
def should_escape_formulae(self) -> bool: ...
44+
skip_export_form: bool
45+
skip_export_form_from_action: bool
5046
def get_export_formats(self) -> list[Format]: ...
51-
def get_export_resource_classes(self) -> list[Resource[_ModelT]]: ...
52-
def choose_export_resource_class(self, form: Form) -> Resource[_ModelT]: ...
53-
def get_export_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
54-
def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> Dataset: ...
47+
def get_export_resource_classes(self, request: HttpRequest) -> list[Resource[_ModelT]]: ...
48+
def choose_export_resource_class(self, form: Form, request: HttpRequest) -> Resource[_ModelT]: ...
49+
def get_export_resource_kwargs(self, request: HttpRequest, **kwargs: Any) -> dict[str, Any]: ...
50+
def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], **kwargs: Any) -> Dataset: ...
5551
def get_export_filename(self, file_format: Format) -> str: ...
52+
def is_skip_export_form_enabled(self) -> bool: ...
53+
def is_skip_export_form_from_action_enabled(self) -> bool: ...
5654

5755
class ExportViewMixin(BaseExportMixin[_ModelT]):
5856
form_class: type[BaseForm] = ...
59-
def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ...
57+
def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], **kwargs: Any) -> str | bytes: ...
6058
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
6159
def get_form_kwargs(self) -> dict[str, Any]: ...
6260

6361
_FormT = TypeVar("_FormT", bound=BaseForm)
6462

63+
@deprecated("ExportViewFormMixin is deprecated and will be removed in a future release.")
6564
class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): # type: ignore[misc]
6665
def form_valid(self, form: _FormT) -> HttpResponse: ...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from collections.abc import Sequence
2+
from typing import Any, Generic, TypeVar
3+
4+
from django.db.models import Model
5+
6+
from .instance_loaders import BaseInstanceLoader
7+
8+
_ModelT = TypeVar("_ModelT", bound=Model)
9+
10+
class ResourceOptions(Generic[_ModelT]):
11+
model: _ModelT | str
12+
fields: Sequence[str] | None
13+
exclude: Sequence[str] | None
14+
instance_loader_class: type[BaseInstanceLoader] | None
15+
import_id_fields: Sequence[str]
16+
export_order: Sequence[str] | None
17+
import_order: Sequence[str] | None
18+
widgets: dict[str, Any] | None
19+
use_transactions: bool | None
20+
skip_unchanged: bool
21+
report_skipped: bool
22+
clean_model_instances: bool
23+
chunk_size: int | None
24+
skip_diff: bool
25+
skip_html_diff: bool
26+
use_bulk: bool
27+
batch_size: int
28+
force_init_instance: bool
29+
using_db: str | None
30+
store_row_values: bool
31+
store_instance: bool
32+
use_natural_foreign_keys: bool

‎stubs/django-import-export/import_export/resources.pyi

+34-98
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,21 @@ from logging import Logger
66
from typing import Any, ClassVar, Generic, Literal, NoReturn, TypeVar, overload
77
from typing_extensions import TypeAlias, deprecated
88

9-
from django.db.models import Field as DjangoField, ForeignObjectRel, Model, QuerySet
9+
from django.db.models import Field as DjangoField, Model, QuerySet
1010
from django.utils.safestring import SafeString
1111

12+
from .declarative import DeclarativeMetaclass, ModelDeclarativeMetaclass
1213
from .fields import Field
1314
from .instance_loaders import BaseInstanceLoader
15+
from .options import ResourceOptions
1416
from .results import Error, Result, RowResult
1517
from .widgets import ForeignKeyWidget, ManyToManyWidget, Widget
1618

1719
Dataset: TypeAlias = _typeshed.Incomplete # tablib.Dataset
1820
logger: Logger
1921

20-
@overload
21-
def get_related_model(field: ForeignObjectRel) -> Model: ...
22-
@overload
23-
def get_related_model(field: DjangoField[Any, Any]) -> Model | None: ...
2422
def has_natural_foreign_key(model: Model) -> bool: ...
2523

26-
class ResourceOptions(Generic[_ModelT]):
27-
model: _ModelT
28-
fields: Sequence[str] | None
29-
exclude: Sequence[str] | None
30-
instance_loader_class: type[BaseInstanceLoader] | None
31-
import_id_fields: Sequence[str]
32-
export_order: Sequence[str] | None
33-
widgets: dict[str, Any] | None
34-
use_transactions: bool | None
35-
skip_unchanged: bool
36-
report_skipped: bool
37-
clean_model_instances: bool
38-
chunk_size: int | None
39-
skip_diff: bool
40-
skip_html_diff: bool
41-
use_bulk: bool
42-
batch_size: int
43-
force_init_instance: bool
44-
using_db: str | None
45-
store_row_values: bool
46-
store_instance: bool
47-
use_natural_foreign_keys: bool
48-
49-
class DeclarativeMetaclass(type):
50-
def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ...
51-
5224
class Diff:
5325
left: list[str]
5426
right: list[str]
@@ -78,6 +50,7 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass):
7850
def get_db_connection_name(self) -> str: ...
7951
def get_use_transactions(self) -> bool: ...
8052
def get_chunk_size(self) -> int: ...
53+
@deprecated("The 'get_fields()' method is deprecated and will be removed in a future release.")
8154
def get_fields(self, **kwargs: Any) -> list[Field]: ...
8255
def get_field_name(self, field: Field) -> str: ...
8356
def init_instance(self, row: dict[str, Any] | None = None) -> _ModelT: ...
@@ -105,56 +78,35 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass):
10578
def validate_instance(
10679
self, instance: _ModelT, import_validation_errors: dict[str, Any] | None = None, validate_unique: bool = True
10780
) -> None: ...
108-
def save_instance(
109-
self, instance: _ModelT, is_create: bool, using_transactions: bool = True, dry_run: bool = False
110-
) -> None: ...
111-
def before_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ...
112-
def after_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ...
113-
def delete_instance(self, instance: _ModelT, using_transactions: bool = True, dry_run: bool = False) -> None: ...
114-
def before_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ...
115-
def after_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ...
116-
def import_field(self, field: Field, obj: _ModelT, data: dict[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ...
81+
# For all the definitions below (from `save_instance()` to `import_row()`), `**kwargs` should contain:
82+
# dry_run: bool, use_transactions: bool, row_number: int, retain_instance_in_row_result: bool.
83+
# Users are free to pass extra arguments in `import_data()`so PEP 728 can probably be leveraged here.
84+
def save_instance(self, instance: _ModelT, is_create: bool, row: dict[str, Any], **kwargs: Any) -> None: ...
85+
def do_instance_save(self, instance: _ModelT) -> None: ...
86+
def before_save_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
87+
def after_save_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
88+
def delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
89+
def before_delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
90+
def after_delete_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
91+
def import_field(self, field: Field, instance: _ModelT, row: dict[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ...
11792
def get_import_fields(self) -> list[Field]: ...
118-
def import_obj(self, obj: _ModelT, data: dict[str, Any], dry_run: bool, **kwargs: Any) -> None: ...
119-
def save_m2m(self, obj: _ModelT, data: dict[str, Any], using_transactions: bool, dry_run: bool) -> None: ...
93+
def import_instance(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
94+
def save_m2m(self, instance: _ModelT, row: dict[str, Any], **kwargs: Any) -> None: ...
12095
def for_delete(self, row: dict[str, Any], instance: _ModelT) -> bool: ...
12196
def skip_row(
12297
self, instance: _ModelT, original: _ModelT, row: dict[str, Any], import_validation_errors: dict[str, Any] | None = None
12398
) -> bool: ...
12499
def get_diff_headers(self) -> list[str]: ...
125-
def before_import(self, dataset: Dataset, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ...
126-
def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ...
127-
def before_import_row(self, row: dict[str, Any], row_number: int | None = None, **kwargs: Any) -> None: ...
128-
def after_import_row(
129-
self, row: dict[str, Any], row_result: RowResult, row_number: int | None = None, **kwargs: Any
130-
) -> None: ...
131-
def after_import_instance(self, instance: _ModelT, new: bool, row_number: int | None = None, **kwargs: Any) -> None: ...
100+
def before_import(self, dataset: Dataset, **kwargs: Any) -> None: ...
101+
def after_import(self, dataset: Dataset, result: Result, **kwargs: Any) -> None: ...
102+
def before_import_row(self, row: dict[str, Any], **kwargs: Any) -> None: ...
103+
def after_import_row(self, row: dict[str, Any], row_result: RowResult, **kwargs: Any) -> None: ...
104+
def after_init_instance(self, instance: _ModelT, new: bool, row: dict[str, Any], **kwargs: Any) -> None: ...
132105
@overload
133106
def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[True]) -> NoReturn: ...
134107
@overload
135108
def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[False] = ...) -> None: ...
136-
@overload
137-
@deprecated("raise_errors argument is deprecated and will be removed in a future release.")
138-
def import_row(
139-
self,
140-
row: dict[str, Any],
141-
instance_loader: BaseInstanceLoader,
142-
using_transactions: bool = True,
143-
dry_run: bool = False,
144-
*,
145-
raise_errors: bool,
146-
**kwargs: Any,
147-
) -> RowResult: ...
148-
@overload
149-
def import_row(
150-
self,
151-
row: dict[str, Any],
152-
instance_loader: BaseInstanceLoader,
153-
using_transactions: bool = True,
154-
dry_run: bool = False,
155-
raise_errors: None = None,
156-
**kwargs: Any,
157-
) -> RowResult: ...
109+
def import_row(self, row: dict[str, Any], instance_loader: BaseInstanceLoader, **kwargs: Any) -> RowResult: ...
158110
def import_data(
159111
self,
160112
dataset: Dataset,
@@ -165,44 +117,28 @@ class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass):
165117
rollback_on_validation_errors: bool = False,
166118
**kwargs: Any,
167119
) -> Result: ...
168-
@overload
169-
@deprecated("rollback_on_validation_errors argument is deprecated and will be removed in a future release.")
170120
def import_data_inner(
171121
self,
172122
dataset: Dataset,
173123
dry_run: bool,
174124
raise_errors: bool,
175125
using_transactions: bool,
176126
collect_failed_rows: bool,
177-
rollback_on_validation_errors: bool,
178-
**kwargs: Any,
179-
) -> Result: ...
180-
@overload
181-
def import_data_inner(
182-
self,
183-
dataset: Dataset,
184-
dry_run: bool,
185-
raise_errors: bool,
186-
using_transactions: bool,
187-
collect_failed_rows: bool,
188-
rollback_on_validation_errors: None = None,
189127
**kwargs: Any,
190128
) -> Result: ...
129+
def get_import_order(self) -> tuple[str, ...]: ...
191130
def get_export_order(self) -> tuple[str, ...]: ...
192-
def before_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> None: ...
193-
def after_export(self, queryset: QuerySet[_ModelT], data: Dataset, *args: Any, **kwargs: Any) -> None: ...
194-
def filter_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ...
195-
def export_field(self, field: Field, obj: _ModelT) -> str: ...
196-
def get_export_fields(self) -> list[Field]: ...
197-
def export_resource(self, obj: _ModelT) -> list[str]: ...
198-
def get_export_headers(self) -> list[str]: ...
131+
def before_export(self, queryset: QuerySet[_ModelT], **kwargs: Any) -> None: ...
132+
def after_export(self, queryset: QuerySet[_ModelT], dataset: Dataset, **kwargs: Any) -> None: ...
133+
def filter_export(self, queryset: QuerySet[_ModelT], **kwargs: Any) -> QuerySet[_ModelT]: ...
134+
def export_field(self, field: Field, instance: _ModelT, **kwargs: Any) -> str: ...
135+
def get_export_fields(self, selected_fields: Sequence[str] | None = None) -> list[Field]: ...
136+
def export_resource(self, instance: _ModelT, selected_fields: Sequence[str] | None = None, **kwargs: Any) -> list[str]: ...
137+
def get_export_headers(self, selected_fields: Sequence[str] | None = None) -> list[str]: ...
199138
def get_user_visible_headers(self) -> list[str]: ...
200139
def get_user_visible_fields(self) -> list[str]: ...
201140
def iter_queryset(self, queryset: QuerySet[_ModelT]) -> Iterator[_ModelT]: ...
202-
def export(self, *args: Any, queryset: QuerySet[_ModelT] | None = None, **kwargs: Any) -> Dataset: ...
203-
204-
class ModelDeclarativeMetaclass(DeclarativeMetaclass):
205-
def __new__(cls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]) -> _typeshed.Self: ...
141+
def export(self, queryset: QuerySet[_ModelT] | None = None, **kwargs: Any) -> Dataset: ...
206142

207143
class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass):
208144
DEFAULT_RESOURCE_FIELD: ClassVar[type[Field]] = ...
@@ -214,12 +150,12 @@ class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass):
214150
@classmethod
215151
def widget_from_django_field(cls, f: DjangoField[Any, Any], default: type[Widget] = ...) -> type[Widget]: ...
216152
@classmethod
217-
def widget_kwargs_for_field(self, field_name: str) -> dict[str, Any]: ...
153+
def widget_kwargs_for_field(cls, field_name: str, django_field: DjangoField[Any, Any]) -> dict[str, Any]: ...
218154
@classmethod
219155
def field_from_django_field(cls, field_name: str, django_field: DjangoField[Any, Any], readonly: bool) -> Field: ...
220156
def get_queryset(self) -> QuerySet[_ModelT]: ...
221157
def init_instance(self, row: dict[str, Any] | None = None) -> _ModelT: ...
222-
def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ...
158+
def after_import(self, dataset: Dataset, result: Result, **kwargs: Any) -> None: ...
223159
@classmethod
224160
def get_display_name(cls) -> str: ...
225161

‎stubs/django-import-export/import_export/results.pyi

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from _typeshed import Incomplete
22
from collections import OrderedDict
33
from collections.abc import Iterator
4+
from functools import cached_property
45
from typing import Any, ClassVar, Literal
56
from typing_extensions import TypeAlias
67

@@ -11,9 +12,11 @@ Dataset: TypeAlias = Incomplete # tablib.Dataset
1112

1213
class Error:
1314
error: Exception
14-
traceback: str
1515
row: dict[str, Any]
16-
def __init__(self, error: Exception, traceback: str | None = None, row: dict[str, Any] | None = None) -> None: ...
16+
number: int | None
17+
def __init__(self, error: Exception, row: dict[str, Any] | None = None, number: int | None = None) -> None: ...
18+
@cached_property
19+
def traceback(self) -> str: ...
1720

1821
_ImportType: TypeAlias = Literal["update", "new", "delete", "skip", "error", "invalid"]
1922

@@ -34,9 +37,20 @@ class RowResult:
3437
object_repr: str | None
3538
instance: Model
3639
original: Model
37-
new_record: bool | None
3840
def __init__(self) -> None: ...
3941
def add_instance_info(self, instance: Model) -> None: ...
42+
def is_update(self) -> bool: ...
43+
def is_new(self) -> bool: ...
44+
def is_delete(self) -> bool: ...
45+
def is_skip(self) -> bool: ...
46+
def is_error(self) -> bool: ...
47+
def is_invalid(self) -> bool: ...
48+
def is_valid(self) -> bool: ...
49+
50+
class ErrorRow:
51+
number: int
52+
errors: list[Error]
53+
def __init__(self, number: int, errors: list[Error]) -> None: ...
4054

4155
class InvalidRow:
4256
number: int
@@ -56,6 +70,7 @@ class Result:
5670
diff_headers: list[str]
5771
rows: list[RowResult]
5872
invalid_rows: list[InvalidRow]
73+
error_rows: list[ErrorRow]
5974
failed_dataset: Dataset
6075
totals: OrderedDict[_ImportType, int]
6176
total_rows: int
@@ -66,6 +81,7 @@ class Result:
6681
def add_dataset_headers(self, headers: list[str] | None) -> None: ...
6782
def append_failed_row(self, row: dict[str, Any], error: Exception) -> None: ...
6883
def append_invalid_row(self, number: int, row: dict[str, Any], validation_error: ValidationError) -> None: ...
84+
def append_error_row(self, number: int, row: dict[str, Any], errors: list[Error]) -> None: ...
6985
def increment_row_result_total(self, row_result: RowResult) -> None: ...
7086
def row_errors(self) -> list[tuple[int, Any]]: ...
7187
def has_errors(self) -> bool: ...
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from collections.abc import Callable
21
from types import TracebackType
3-
from typing import Any, TypeVar
2+
from typing import Any, overload
43

4+
from django.db.models import Field as DjangoField, ForeignObjectRel, Model
55
from django.db.transaction import Atomic
66

7-
_C = TypeVar("_C", bound=Callable[..., Any])
8-
97
class atomic_if_using_transaction:
108
using_transactions: bool
119
context_manager: Atomic
@@ -15,4 +13,7 @@ class atomic_if_using_transaction:
1513
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None
1614
) -> None: ...
1715

18-
def original(method: _C) -> _C: ...
16+
@overload
17+
def get_related_model(field: ForeignObjectRel) -> Model: ...
18+
@overload
19+
def get_related_model(field: DjangoField[Any, Any]) -> Model | None: ...

‎stubs/django-import-export/import_export/widgets.pyi

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,56 @@
11
from collections.abc import Mapping
22
from datetime import datetime
3-
from typing import Any, ClassVar, Generic, TypeVar
3+
from typing import Any, ClassVar, Generic, TypeVar, overload
4+
from typing_extensions import deprecated
45

56
from django.db.models import Model, QuerySet
67

78
def format_datetime(value: datetime, datetime_format: str) -> str: ...
89

910
class Widget:
11+
coerce_to_string: bool
12+
def __init__(self, coerce_to_string: bool = True) -> None: ...
1013
def clean(self, value: Any, row: Mapping[str, Any] | None = None, **kwargs: Any) -> Any: ...
11-
def render(self, value: Any, obj: Model | None = None) -> Any: ...
14+
@overload
15+
@deprecated("The 'obj' parameter is deprecated and will be removed in a future release.")
16+
def render(self, value: Any, obj: Model, **kwargs: Any) -> Any: ...
17+
@overload
18+
def render(self, value: Any, obj: None = None, **kwargs: Any) -> Any: ...
1219

1320
class NumberWidget(Widget):
14-
coerce_to_string: bool
15-
def __init__(self, coerce_to_string: bool = False) -> None: ...
1621
def is_empty(self, value: Any) -> bool: ...
17-
def render(self, value: Any, obj: Model | None = None) -> Any: ...
1822

1923
class FloatWidget(NumberWidget): ...
2024
class IntegerWidget(NumberWidget): ...
2125
class DecimalWidget(NumberWidget): ...
2226

2327
class CharWidget(Widget):
24-
coerce_to_string: bool
2528
allow_blank: bool
26-
def __init__(self, coerce_to_string: bool = False, allow_blank: bool = False) -> None: ...
29+
def __init__(self, coerce_to_string: bool = True, allow_blank: bool = True) -> None: ...
2730

2831
class BooleanWidget(Widget):
2932
TRUE_VALUES: ClassVar[list[str | int | bool]]
3033
FALSE_VALUES: ClassVar[list[str | int | bool]]
3134
NULL_VALUES: ClassVar[list[str | None]]
35+
def __init__(self, coerce_to_string: bool = True) -> None: ...
3236

3337
class DateWidget(Widget):
3438
formats: tuple[str, ...]
35-
def __init__(self, format: str | None = None) -> None: ...
39+
def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ...
3640

3741
class DateTimeWidget(Widget):
3842
formats: tuple[str, ...]
39-
def __init__(self, format: str | None = None) -> None: ...
43+
def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ...
4044

4145
class TimeWidget(Widget):
4246
formats: tuple[str, ...]
43-
def __init__(self, format: str | None = None) -> None: ...
47+
def __init__(self, format: str | None = None, coerce_to_string: bool = True) -> None: ...
4448

4549
class DurationWidget(Widget): ...
4650

4751
class SimpleArrayWidget(Widget):
4852
separator: str
49-
def __init__(self, separator: str | None = None) -> None: ...
53+
def __init__(self, separator: str | None = None, coerce_to_string: bool = True) -> None: ...
5054

5155
class JSONWidget(Widget): ...
5256

@@ -55,9 +59,13 @@ _ModelT = TypeVar("_ModelT", bound=Model)
5559
class ForeignKeyWidget(Widget, Generic[_ModelT]):
5660
model: _ModelT
5761
field: str
62+
key_is_id: bool
5863
use_natural_foreign_keys: bool
59-
def __init__(self, model: _ModelT, field: str = "pk", use_natural_foreign_keys: bool = False, **kwargs: Any) -> None: ...
64+
def __init__(
65+
self, model: _ModelT, field: str = "pk", use_natural_foreign_keys: bool = False, key_is_id: bool = False, **kwargs: Any
66+
) -> None: ...
6067
def get_queryset(self, value: Any, row: Mapping[str, Any], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ...
68+
def get_lookup_kwargs(self, value: Any, row: Mapping[str, Any] | None = None, **kwargs: Any) -> dict[str, Any]: ...
6169

6270
class ManyToManyWidget(Widget, Generic[_ModelT]):
6371
model: _ModelT

‎stubs/django-import-export/management/__init__.pyi

Whitespace-only changes.

‎stubs/django-import-export/management/commands/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.core.management.base import BaseCommand
2+
3+
class Command(BaseCommand): ...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.core.management.base import BaseCommand
2+
3+
class Command(BaseCommand): ...

0 commit comments

Comments
 (0)
Please sign in to comment.