Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forms and exceptions improvements #259

Merged
merged 9 commits into from
Aug 20, 2024
27 changes: 16 additions & 11 deletions django-stubs/core/exceptions.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from collections.abc import Iterator, Mapping
from typing import Any

from django.forms.utils import ErrorDict

class FieldDoesNotExist(Exception): ...
class AppRegistryNotReady(Exception): ...

Expand All @@ -16,6 +14,7 @@ class SuspiciousFileOperation(SuspiciousOperation): ...
class DisallowedHost(SuspiciousOperation): ...
class DisallowedRedirect(SuspiciousOperation): ...
class TooManyFieldsSent(SuspiciousOperation): ...
class TooManyFilesSent(SuspiciousOperation): ...
class RequestDataTooBig(SuspiciousOperation): ...
class RequestAborted(Exception): ...
class BadRequest(Exception): ...
Expand All @@ -28,14 +27,19 @@ class FieldError(Exception): ...
NON_FIELD_ERRORS: str

class ValidationError(Exception):
error_dict: Any = ...
error_list: Any = ...
message: Any = ...
code: Any = ...
params: Any = ...
error_dict: dict[str, list[ValidationError]] | None
error_list: list[ValidationError] | None
message: str | None
code: str | None
params: Mapping[str, Any] | None
def __init__(
self,
message: Any,
message: (
ValidationError
| dict[str, ValidationError | list[str]]
| list[ValidationError | str]
| str
),
code: str | None = ...,
params: Mapping[str, Any] | None = ...,
) -> None: ...
Expand All @@ -44,9 +48,10 @@ class ValidationError(Exception):
@property
def messages(self) -> list[str]: ...
def update_error_dict(
self, error_dict: Mapping[str, Any]
) -> dict[str, list[ValidationError]] | ErrorDict: ...
def __iter__(self) -> Iterator[tuple[str, list[str]] | str]: ...
self, error_dict: Mapping[str, list[ValidationError]]
) -> Mapping[str, list[ValidationError]]: ...
def __iter__(self) -> Iterator[tuple[str, list[ValidationError]] | str]: ...

class EmptyResultSet(Exception): ...
class FullResultSet(Exception): ...
class SynchronousOnlyOperation(Exception): ...
47 changes: 21 additions & 26 deletions django-stubs/forms/forms.pyi
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
from collections.abc import Iterator, Mapping
from typing import Any
from typing import Any, ClassVar

from django.core.exceptions import ValidationError as ValidationError
from django.core.files import uploadedfile
from django.db.models.options import Options
from django.forms.boundfield import BoundField
from django.forms.fields import Field
from django.forms.renderers import BaseRenderer
from django.forms.utils import ErrorDict, ErrorList
from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin
from django.forms.widgets import Media, MediaDefiningClass
from django.utils.datastructures import MultiValueDict
from django.utils.safestring import SafeText

class DeclarativeFieldsMetaclass(MediaDefiningClass): ...

class BaseForm:
_meta: Options[Any]
default_renderer: type[BaseRenderer] = ...
field_order: list[str] | None = ...
use_required_attribute: bool = ...
is_bound: bool = ...
data: dict[str, Any] = ...
files: MultiValueDict[str, uploadedfile.UploadedFile] = ...
auto_id: bool | str = ...
initial: dict[str, Any] = ...
error_class: type[ErrorList] = ...
prefix: str | None = ...
label_suffix: str = ...
empty_permitted: bool = ...
fields: dict[str, Any] = ...
renderer: BaseRenderer = ...
cleaned_data: dict[str, Any] = ...
class BaseForm(RenderableFormMixin):
default_renderer: type[BaseRenderer]
field_order: list[str] | None
use_required_attribute: bool
is_bound: bool
data: dict[str, Any]
files: MultiValueDict[str, uploadedfile.UploadedFile]
auto_id: bool | str
initial: dict[str, Any]
error_class: type[ErrorList]
prefix: str | None
label_suffix: str
empty_permitted: bool
fields: dict[str, Field]
renderer: BaseRenderer
cleaned_data: dict[str, Any]
def __init__(
self,
data: Mapping[str, Any] | None = ...,
Expand All @@ -53,9 +51,6 @@ class BaseForm:
def is_valid(self) -> bool: ...
def add_prefix(self, field_name: str) -> str: ...
def add_initial_prefix(self, field_name: str) -> str: ...
def as_table(self) -> SafeText: ...
def as_ul(self) -> SafeText: ...
def as_p(self) -> SafeText: ...
def non_field_errors(self) -> ErrorList: ...
def add_error(self, field: str | None, error: ValidationError | str) -> None: ...
def has_error(self, field: str, code: str | None = ...) -> bool: ...
Expand All @@ -79,6 +74,6 @@ class BaseForm:
errors_on_separate_row: bool,
) -> SafeText: ...

class Form(BaseForm):
base_fields: dict[str, Field]
declared_fields: dict[str, Field]
class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass):
base_fields: ClassVar[dict[str, Field]]
declared_fields: ClassVar[dict[str, Field]]
31 changes: 19 additions & 12 deletions django-stubs/forms/models.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from collections.abc import (
Sequence,
)
from datetime import datetime
from typing import Any, ClassVar, TypeVar
from typing import Any, ClassVar, Protocol, TypeVar
from typing_extensions import Literal
from unittest.mock import MagicMock
from uuid import UUID
Expand All @@ -33,6 +33,11 @@ _ErrorMessages = dict[str, dict[str, str]]

_M = TypeVar("_M", bound=Model)

# Modeled from example:
# https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/#overriding-the-default-fields
class FormFieldCallback(Protocol):
def __call__(self, db_field: models.Field[Any, Any], **kwargs: Any) -> Field: ...

def construct_instance(
form: BaseForm,
instance: _M,
Expand All @@ -42,12 +47,13 @@ def construct_instance(
def model_to_dict(
instance: Model, fields: _Fields | None = ..., exclude: _Fields | None = ...
) -> dict[str, Any]: ...
def apply_limit_choices_to_to_formfield(formfield: Field) -> None: ...
def fields_for_model(
model: type[Model],
fields: _Fields | None = ...,
exclude: _Fields | None = ...,
widgets: dict[str, type[Input]] | dict[str, Widget] | None = ...,
formfield_callback: Callable[..., Any] | str | None = ...,
formfield_callback: FormFieldCallback | None = ...,
localized_fields: tuple[str] | str | None = ...,
labels: _Labels | None = ...,
help_texts: dict[str, str] | None = ...,
Expand All @@ -58,15 +64,16 @@ def fields_for_model(
) -> dict[str, Any]: ...

class ModelFormOptions:
model: type[Model] | None = ...
fields: _Fields | None = ...
exclude: _Fields | None = ...
widgets: dict[str, Widget | Input] | None = ...
localized_fields: tuple[str] | str | None = ...
labels: _Labels | None = ...
help_texts: dict[str, str] | None = ...
error_messages: _ErrorMessages | None = ...
field_classes: dict[str, type[Field]] | None = ...
model: type[Model] | None
fields: _Fields | None
exclude: _Fields | None
widgets: dict[str, Widget | Input] | None
localized_fields: tuple[str] | str | None
labels: _Labels | None
help_texts: dict[str, str] | None
error_messages: _ErrorMessages | None
field_classes: dict[str, type[Field]] | None
formfield_callback: FormFieldCallback | None
def __init__(self, options: type | None = ...) -> None: ...

class ModelFormMetaclass(DeclarativeFieldsMetaclass): ...
Expand All @@ -92,7 +99,7 @@ class BaseModelForm(BaseForm):
def save(self, commit: bool = ...) -> Any: ...

class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass):
base_fields: ClassVar[dict[str, Field]] = ...
_meta: ClassVar[ModelFormOptions]

def modelform_factory(
model: type[Model],
Expand Down
49 changes: 38 additions & 11 deletions django-stubs/forms/utils.pyi
Original file line number Diff line number Diff line change
@@ -1,34 +1,61 @@
from collections import UserList
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
from datetime import datetime
from typing import Any

from django.core.exceptions import ValidationError
from django.forms.renderers import BaseRenderer
from django.utils.safestring import SafeText

def pretty_name(name: str) -> str: ...
def flatatt(attrs: dict[str, Any]) -> SafeText: ...

class ErrorDict(dict[str, Any]):
class RenderableMixin:
def get_context(self) -> Mapping[str, Any]: ...
def render(
self,
template_name: str | None = ...,
context: Mapping[str, Any] | None = ...,
renderer: BaseRenderer | None = ...,
) -> SafeText: ...

class RenderableFormMixin(RenderableMixin):
def as_p(self) -> SafeText: ...
def as_table(self) -> SafeText: ...
def as_ul(self) -> SafeText: ...
def as_div(self) -> SafeText: ...

class RenderableErrorMixin(RenderableMixin):
def as_json(self, escape_html: bool = ...) -> str: ...
def as_text(self) -> SafeText: ...
def as_ul(self) -> SafeText: ...

class ErrorDict(dict[str, ErrorList], RenderableErrorMixin):
template_name: str
template_name_text: str
template_name_ul: str
renderer: BaseRenderer
def __init__(
self, *args: Any, renderer: BaseRenderer | None = ..., **kwargs: Any
): ...
def as_data(self) -> dict[str, list[ValidationError]]: ...
def get_json_data(self, escape_html: bool = ...) -> dict[str, Any]: ...
def as_json(self, escape_html: bool = ...) -> str: ...
def as_ul(self) -> str: ...
def as_text(self) -> str: ...

class ErrorList(UserList[Any]):
class ErrorList(UserList[ValidationError | str], RenderableErrorMixin):
template_name: str
template_name_text: str
template_name_ul: str
data: list[ValidationError | str]
error_class: str = ...
error_class: str
renderer: BaseRenderer
def __init__(
self,
initlist: ErrorList | Sequence[str | Exception] | None = ...,
initlist: Sequence[str | Exception] | None = ...,
error_class: str | None = ...,
renderer: BaseRenderer | None = None,
) -> None: ...
def as_data(self) -> list[ValidationError]: ...
def get_json_data(self, escape_html: bool = ...) -> list[dict[str, str]]: ...
def as_json(self, escape_html: bool = ...) -> str: ...
def as_ul(self) -> str: ...
def as_text(self) -> str: ...

def from_current_timezone(value: datetime) -> datetime: ...
def to_current_timezone(value: datetime) -> datetime: ...
Loading