Skip to content
Merged
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
1 change: 1 addition & 0 deletions django-stubs/db/models/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ from .fields.files import FieldFile as FieldFile
from .fields.files import FileDescriptor as FileDescriptor
from .fields.files import FileField as FileField
from .fields.files import ImageField as ImageField
from .fields.generated import GeneratedField as GeneratedField
from .fields.json import JSONField as JSONField
from .fields.proxy import OrderWrt as OrderWrt
from .fields.related import ForeignKey as ForeignKey
Expand Down
83 changes: 83 additions & 0 deletions django-stubs/db/models/fields/generated.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from collections.abc import Iterable
from typing import Any, Literal, Never, TypeVar, overload

from django.db.models import Combinable, Expression, ForeignObjectRel
from django.db.models.expressions import Col

from . import Field, _ErrorMessagesToOverride, _ValidatorCallable
from .mixins import CheckFieldDefaultMixin

_GT = TypeVar("_GT", bound=Any | None)

class GeneratedField(CheckFieldDefaultMixin, Field[Never, _GT]):
generated: Literal[True]
db_returning: Literal[True]
expression: Combinable | Expression

@overload
def __new__(
cls,
*,
expression: Combinable | Expression,
output_field: Field[Any, _GT],
db_persist: bool,
#
verbose_name: str | None = ...,
name: str | None = ...,
primary_key: bool = ...,
max_length: int | None = ...,
unique: bool = ...,
blank: Literal[True] = ...,
null: Literal[False] = False,
db_index: bool = ...,
rel: ForeignObjectRel | None = ...,
editable: Literal[False] = ...,
serialize: bool = ...,
unique_for_date: str | None = ...,
unique_for_month: str | None = ...,
unique_for_year: str | None = ...,
choices: Iterable[tuple[Any, str] | tuple[str, Iterable[tuple[Any, str]]]] = ...,
help_text: str = ...,
db_column: str | None = ...,
db_tablespace: str | None = ...,
auto_created: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesToOverride | None = ...,
db_comment: str | None = ...,
) -> GeneratedField[_GT]: ...
@overload
def __new__(
cls,
*,
expression: Combinable | Expression,
output_field: Field[Any, _GT],
db_persist: bool,
#
verbose_name: str | None = ...,
name: str | None = ...,
primary_key: bool = ...,
max_length: int | None = ...,
unique: bool = ...,
blank: Literal[True] = ...,
null: Literal[True],
db_index: bool = ...,
rel: ForeignObjectRel | None = ...,
editable: Literal[False] = ...,
serialize: bool = ...,
unique_for_date: str | None = ...,
unique_for_month: str | None = ...,
unique_for_year: str | None = ...,
choices: Iterable[tuple[Any, str] | tuple[str, Iterable[tuple[Any, str]]]] = ...,
help_text: str = ...,
db_column: str | None = ...,
db_tablespace: str | None = ...,
auto_created: bool = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: _ErrorMessagesToOverride | None = ...,
db_comment: str | None = ...,
) -> GeneratedField[_GT | None]: ...
@property
def cached_col(self) -> Col: ...
def generated_sql(self, connection: Any) -> tuple[str, list[Any]]: ...

__all__ = ["GeneratedField"]
20 changes: 20 additions & 0 deletions tests/trout/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
)
from psycopg2.extras import execute_values

from django.db.models.fields.generated import GeneratedField


class CustomChoices(models.Choices):
A = "B", "A"
Expand Down Expand Up @@ -269,6 +271,18 @@ class Comment(models.Model):
object_id_field="generic_id",
)

generated_field = GeneratedField(
expression=models.F("integer") + 1,
output_field=models.IntegerField(),
db_persist=True,
)
nullable_generated_field = GeneratedField(
expression=models.F("integer_nullable") + 1,
output_field=models.IntegerField(null=True),
db_persist=True,
null=True,
)


def process_non_nullable(
x: Union[
Expand Down Expand Up @@ -669,6 +683,12 @@ def main() -> None:
if not isinstance(comment.other_metadata, dict):
print() # type: ignore [unreachable]

process_non_nullable(comment.generated_field)
if isinstance(comment.nullable_generated_field, type(None)):
print(comment.nullable_generated_field)
if not isinstance(comment.generated_field, int):
print() # type: ignore [unreachable]


async def main_async() -> None:
comment = await Comment.objects.aget(pk=123)
Expand Down