Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion django-stubs/contrib/gis/db/models/fields.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ from django.core.validators import _ValidatorCallable
from django.db.models import Model
from django.db.models.expressions import Combinable, Expression
from django.db.models.fields import NOT_PROVIDED, Field, _ErrorMessagesMapping
from django.forms.widgets import Widget
from django.utils.choices import _Choices
from django.utils.functional import _StrOrPromise
from typing_extensions import TypeVar, override
Expand Down Expand Up @@ -125,10 +126,23 @@ class GeometryField(BaseSpatialField[_ST, _GT]):
self,
*,
form_class: type[forms.GeometryField] | None = ...,
choices_form_class: type[forms.GeometryField] | None = ...,
required: bool = ...,
widget: Widget | type[Widget] | None = ...,
label: _StrOrPromise | None = ...,
initial: Any | None = ...,
help_text: _StrOrPromise = ...,
error_messages: _ErrorMessagesMapping | None = ...,
show_hidden_initial: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: str | None = ...,
# GeometryField adds `geom_type` and `srid`
geom_type: str = ...,
srid: Any = ...,
**kwargs: Any,
) -> forms.GeometryField: ...
) -> forms.GeometryField | None: ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the base class, subclasses of this are allowed to return None, even though this particular method doesn't.


class PointField(GeometryField[_ST, _GT]):
_pyi_private_set_type: Point | Combinable
Expand Down
2 changes: 0 additions & 2 deletions django-stubs/contrib/postgres/fields/array.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,5 @@ class ArrayField(CheckPostgresInstalledMixin, CheckFieldDefaultMixin, Field[_ST,
def get_placeholder(self, value: Unused, compiler: Unused, connection: BaseDatabaseWrapper) -> str: ...
@override
def get_transform(self, name: str) -> type[Transform] | None: ...
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]

__all__ = ["ArrayField"]
2 changes: 0 additions & 2 deletions django-stubs/contrib/postgres/fields/hstore.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ from typing_extensions import override
class HStoreField(CheckPostgresInstalledMixin, CheckFieldDefaultMixin, Field):
@override
def get_transform(self, name: str) -> Any: ...
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]

class KeyTransform(Transform):
output_field: ClassVar[TextField]
Expand Down
2 changes: 0 additions & 2 deletions django-stubs/contrib/postgres/fields/ranges.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ class RangeField(CheckPostgresInstalledMixin, models.Field[Any, _RangeT]):
def to_python(self, value: Any) -> Any: ...
@override
def value_to_string(self, obj: models.Model) -> str | None: ... # type: ignore[override]
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]

class ContinuousRangeField(RangeField[_RangeT]):
default_bounds: str
Expand Down
58 changes: 16 additions & 42 deletions django-stubs/db/models/fields/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ from django.db.models.fields.reverse_related import ForeignObjectRel
from django.db.models.query import _OrderByFieldName
from django.db.models.query_utils import Q, RegisterLookupMixin
from django.db.models.sql.compiler import SQLCompiler, _AsSqlType, _ParamsT
from django.forms.widgets import Widget
from django.utils.choices import BlankChoiceIterator, _Choice, _ChoiceNamedGroup, _ChoicesCallable, _ChoicesInput
from django.utils.datastructures import DictWrapper
from django.utils.functional import _Getter, _StrOrPromise, cached_property
Expand Down Expand Up @@ -250,9 +251,22 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def save_form_data(self, instance: Model, data: Any) -> None: ...
def formfield(
self,
form_class: type[forms.Field] | None = None,
choices_form_class: type[forms.ChoiceField] | None = None,
*,
form_class: type[forms.Field] | None = ...,
choices_form_class: type[forms.ChoiceField] | None = ...,
required: bool = ...,
widget: Widget | type[Widget] | None = ...,
label: _StrOrPromise | None = ...,
initial: Any | None = ...,
help_text: _StrOrPromise = ...,
error_messages: _ErrorMessagesMapping | None = ...,
show_hidden_initial: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: str | None = ...,
**kwargs: Any,
# Subclasses are allowed to return None
) -> forms.Field | None: ...
def value_from_object(self, obj: Model) -> _GT: ...
def slice_expression(self, expression: Expression, start: int, length: int | None) -> Func: ...
Expand All @@ -261,8 +275,6 @@ class IntegerField(Field[_ST, _GT]):
_pyi_private_set_type: float | int | str | Combinable
_pyi_private_get_type: int
_pyi_lookup_exact_type: str | int
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class PositiveIntegerRelDbTypeMixin:
def rel_db_type(self, connection: BaseDatabaseWrapper) -> str: ...
Expand All @@ -271,30 +283,20 @@ class SmallIntegerField(IntegerField[_ST, _GT]): ...

class BigIntegerField(IntegerField[_ST, _GT]):
MAX_BIGINT: ClassVar[int]
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]):
integer_field_class: type[IntegerField]
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField[_ST, _GT]):
integer_field_class: type[SmallIntegerField]
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField[_ST, _GT]):
integer_field_class: type[BigIntegerField]
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class FloatField(Field[_ST, _GT]):
_pyi_private_set_type: float | int | str | Combinable
_pyi_private_get_type: float
_pyi_lookup_exact_type: float
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class DecimalField(Field[_ST, _GT]):
_pyi_private_set_type: str | float | decimal.Decimal | Combinable
Expand Down Expand Up @@ -330,8 +332,6 @@ class DecimalField(Field[_ST, _GT]):
) -> None: ...
@cached_property
def context(self) -> decimal.Context: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class CharField(Field[_ST, _GT]):
_pyi_private_set_type: str | int | Combinable
Expand Down Expand Up @@ -366,8 +366,6 @@ class CharField(Field[_ST, _GT]):
*,
db_collation: str | None = None,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class CommaSeparatedIntegerField(CharField[_ST, _GT]): ...

Expand Down Expand Up @@ -400,13 +398,9 @@ class SlugField(CharField[_ST, _GT]):
db_index: bool = True,
allow_unicode: bool = False,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class EmailField(CharField[_ST, _GT]):
_pyi_private_set_type: str | Combinable
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class URLField(CharField[_ST, _GT]):
def __init__(
Expand Down Expand Up @@ -437,8 +431,6 @@ class URLField(CharField[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesMapping | None = ...,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class TextField(Field[_ST, _GT]):
_pyi_private_set_type: str | Combinable
Expand Down Expand Up @@ -473,15 +465,11 @@ class TextField(Field[_ST, _GT]):
*,
db_collation: str | None = None,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class BooleanField(Field[_ST, _GT]):
_pyi_private_set_type: bool | Combinable
_pyi_private_get_type: bool
_pyi_lookup_exact_type: bool
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class NullBooleanField(BooleanField[_ST, _GT]):
_pyi_private_set_type: bool | Combinable | None # type: ignore[assignment]
Expand Down Expand Up @@ -523,8 +511,6 @@ class GenericIPAddressField(Field[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesMapping | None = ...,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class DateTimeCheckMixin:
def check(self, **kwargs: Any) -> list[CheckMessage]: ...
Expand Down Expand Up @@ -563,8 +549,6 @@ class DateField(DateTimeCheckMixin, Field[_ST, _GT]):
) -> None: ...
@override
def contribute_to_class(self, cls: type[Model], name: str, **kwargs: Any) -> None: ... # type: ignore[override]
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class TimeField(DateTimeCheckMixin, Field[_ST, _GT]):
_pyi_private_set_type: str | time | real_datetime | Combinable
Expand Down Expand Up @@ -596,15 +580,11 @@ class TimeField(DateTimeCheckMixin, Field[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesMapping | None = ...,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class DateTimeField(DateField[_ST, _GT]):
_pyi_private_set_type: str | real_datetime | date | Combinable
_pyi_private_get_type: real_datetime
_pyi_lookup_exact_type: str | real_datetime
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class UUIDField(Field[_ST, _GT]):
_pyi_private_set_type: str | uuid.UUID
Expand Down Expand Up @@ -638,8 +618,6 @@ class UUIDField(Field[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesMapping | None = ...,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class FilePathField(Field[_ST, _GT]):
path: Any
Expand Down Expand Up @@ -676,17 +654,13 @@ class FilePathField(Field[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesMapping | None = ...,
) -> None: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class BinaryField(Field[_ST, _GT]):
_pyi_private_get_type: bytes | memoryview
def get_placeholder(self, value: Any, compiler: SQLCompiler, connection: BaseDatabaseWrapper) -> str: ...

class DurationField(Field[_ST, _GT]):
_pyi_private_get_type: timedelta
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]

class AutoFieldMixin:
db_returning: bool
Expand Down
4 changes: 0 additions & 4 deletions django-stubs/db/models/fields/files.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ class FileField(Field[Any, Any]):
@override
def contribute_to_class(self, cls: type[Model], name: str, **kwargs: Any) -> None: ... # type: ignore[override]
def generate_filename(self, instance: Model | None, filename: _PathCompatible) -> str: ...
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]

class ImageFileDescriptor(FileDescriptor):
field: ImageField
Expand Down Expand Up @@ -145,5 +143,3 @@ class ImageField(FileField):
@override
def __get__(self, instance: Any, owner: Any) -> Self: ...
def update_dimension_fields(self, instance: Model, force: bool = False, *args: Any, **kwargs: Any) -> None: ...
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]
2 changes: 0 additions & 2 deletions django-stubs/db/models/fields/json.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ class JSONField(CheckFieldDefaultMixin, Field[_ST, _GT]):
def get_transform(self, name: str) -> type[Transform] | KeyTransformFactory: ... # type: ignore[override]
@override
def value_to_string(self, obj: Model) -> Any: ...
@override
def formfield(self, **kwargs: Any) -> Any: ... # type: ignore[override]

class DataContains(FieldGetDbPrepValueMixin, PostgresOperatorLookup): ...
class ContainedBy(FieldGetDbPrepValueMixin, PostgresOperatorLookup): ...
Expand Down
47 changes: 41 additions & 6 deletions django-stubs/db/models/fields/related.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ from django.db.models.fields.reverse_related import ManyToOneRel as ManyToOneRel
from django.db.models.fields.reverse_related import OneToOneRel as OneToOneRel
from django.db.models.query_utils import FilteredRelation, PathInfo, Q
from django.db.models.sql.where import WhereNode
from django.forms.widgets import Widget
from django.utils.choices import _Choices
from django.utils.functional import _StrOrPromise, cached_property
from typing_extensions import Self, TypeVar, override
Expand Down Expand Up @@ -89,8 +90,6 @@ class RelatedField(FieldCacheMixin, Field[_ST, _GT]):
def set_attributes_from_rel(self) -> None: ...
def do_related_class(self, other: type[Model], cls: type[Model]) -> None: ...
def get_limit_choices_to(self) -> _LimitChoicesTo: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]
def related_query_name(self) -> str: ...
@property
def target_field(self) -> Field: ...
Expand Down Expand Up @@ -225,7 +224,26 @@ class ForeignKey(ForeignObject[_ST, _GT]):
@override
def contribute_to_related_class(self, cls: type[Model], related: RelatedField) -> None: ...
@override
def formfield(self, *, using: str | None = None, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]
def formfield(
self,
*,
form_class: type[forms.Field] | None = ...,
choices_form_class: type[forms.ChoiceField] | None = ...,
required: bool = ...,
widget: Widget | type[Widget] | None = ...,
label: _StrOrPromise | None = ...,
initial: Any | None = ...,
help_text: _StrOrPromise = ...,
error_messages: _ErrorMessagesMapping | None = ...,
show_hidden_initial: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: str | None = ...,
# ForeignKey adds `using`
using: str | None = ...,
**kwargs: Any,
) -> forms.Field | None: ...
@override
def cast_db_type(self, connection: BaseDatabaseWrapper) -> str | None: ...
def convert_empty_strings(self, value: Any, expression: Expression, connection: BaseDatabaseWrapper) -> Any: ...
Expand Down Expand Up @@ -286,8 +304,6 @@ class OneToOneField(ForeignKey[_ST, _GT]):
@overload
@override
def __get__(self, instance: Any, owner: Any) -> Self: ...
@override
def formfield(self, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]
forward_related_accessor_class: type[ForwardOneToOneDescriptor]
related_accessor_class: type[ReverseOneToOneDescriptor] # type: ignore[assignment]

Expand Down Expand Up @@ -368,7 +384,26 @@ class ManyToManyField(RelatedField[Any, Any], Generic[_To, _Through]):
def m2m_target_field_name(self) -> str: ...
def m2m_reverse_target_field_name(self) -> str: ...
@override
def formfield(self, *, using: str | None = None, **kwargs: Any) -> forms.Field | None: ... # type: ignore[override]
def formfield(
self,
*,
form_class: type[forms.Field] | None = ...,
choices_form_class: type[forms.ChoiceField] | None = ...,
required: bool = ...,
widget: Widget | type[Widget] | None = ...,
label: _StrOrPromise | None = ...,
initial: Any | None = ...,
help_text: _StrOrPromise = ...,
error_messages: _ErrorMessagesMapping | None = ...,
show_hidden_initial: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: str | None = ...,
# ManyToManyField adds `using`
using: str | None = ...,
**kwargs: Any,
) -> forms.Field | None: ...
@cached_property
def path_infos(self) -> list[PathInfo]: ...
@cached_property
Expand Down
5 changes: 5 additions & 0 deletions scripts/stubtest/allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ django.urls.resolvers.ResolverMatch.__iter__
django.template.smartif.key
django.template.smartif.op

# The parameters form_class / choices_form_class are positional at runtime, but
# usage and docs always pass them as keyword args; additionally, all subclasses
# require them to be keyword-only, so require all usage to be keyword-only.
Comment on lines +240 to +242

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting.

django.db.models.fields.Field.formfield

# Field.__get__/__set__ are stub-only for the mypy plugin, they don't exist at runtime
django.db.models.fields.Field.__get__
django.db.models.fields.Field.__set__
Expand Down
Loading