diff --git a/.travis.yml b/.travis.yml index 6040389..419400d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ jobs: script: 'isort --check' before_install: | + sudo apt update + sudo apt install binutils libproj-dev gdal-bin pip install -U pip setuptools wheel install: | pip install -r ./dev-requirements.txt diff --git a/django-stubs/contrib/gis/__init__.pyi b/django-stubs/contrib/gis/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/django-stubs/contrib/gis/db/__init__.pyi b/django-stubs/contrib/gis/db/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/django-stubs/contrib/gis/db/models/__init__.pyi b/django-stubs/contrib/gis/db/models/__init__.pyi new file mode 100644 index 0000000..c930715 --- /dev/null +++ b/django-stubs/contrib/gis/db/models/__init__.pyi @@ -0,0 +1,13 @@ +from django.db.models import * + +from .fields import ( + GeometryField as GeometryField, + LineStringField as LineStringField, + MultiLineStringField as MultiLineStringField, + MultiPointField as MultiPointField, + MultiPolygonField as MultiPolygonField, + PointField as PointField, + PolygonField as PolygonField, + GeometryCollectionField as GeometryCollectionField, + RasterField as RasterField, +) diff --git a/django-stubs/contrib/gis/db/models/fields.pyi b/django-stubs/contrib/gis/db/models/fields.pyi new file mode 100644 index 0000000..ed2a6f4 --- /dev/null +++ b/django-stubs/contrib/gis/db/models/fields.pyi @@ -0,0 +1,91 @@ +from typing import Any, Iterable, NamedTuple, Optional, TypeVar, Union, Tuple + +from django.db.models.fields import Field, _ErrorMessagesToOverride, _FieldChoices, _ValidatorCallable + +_Connection = Any + +# __set__ value type +_ST = TypeVar("_ST") +# __get__ return type +_GT = TypeVar("_GT") + +class SRIDCacheEntry(NamedTuple): + units: Any + units_name: str + geodetic: bool + spheroid: str + +def get_srid_info(srid: int, connection: _Connection) -> SRIDCacheEntry: ... + +class BaseSpatialField(Field[_ST, _GT]): + def __init__( + self, + verbose_name: Optional[Union[str, bytes]] = ..., + srid: int = ..., + spatial_index: bool = ..., + name: Optional[str] = ..., + primary_key: bool = ..., + max_length: Optional[int] = ..., + unique: bool = ..., + blank: bool = ..., + null: bool = ..., + db_index: bool = ..., + default: Any = ..., + editable: bool = ..., + auto_created: bool = ..., + serialize: bool = ..., + unique_for_date: Optional[str] = ..., + unique_for_month: Optional[str] = ..., + unique_for_year: Optional[str] = ..., + choices: Optional[_FieldChoices] = ..., + help_text: str = ..., + db_column: Optional[str] = ..., + db_tablespace: Optional[str] = ..., + validators: Iterable[_ValidatorCallable] = ..., + error_messages: Optional[_ErrorMessagesToOverride] = ..., + ): ... + def spheroid(self, connection: _Connection) -> str: ... + def units(self, connection: _Connection) -> Any: ... + def units_name(self, connection: _Connection) -> str: ... + def geodetic(self, connection: _Connection) -> bool: ... + +class GeometryField(BaseSpatialField): + def __init__( + self, + verbose_name: Optional[Union[str, bytes]] = ..., + dim: int = ..., + geography: bool = ..., + extent: Tuple[float, float, float, float] = ..., + tolerance: float = ..., + srid: int = ..., + spatial_index: bool = ..., + name: Optional[str] = ..., + primary_key: bool = ..., + max_length: Optional[int] = ..., + unique: bool = ..., + blank: bool = ..., + null: bool = ..., + db_index: bool = ..., + default: Any = ..., + editable: bool = ..., + auto_created: bool = ..., + serialize: bool = ..., + unique_for_date: Optional[str] = ..., + unique_for_month: Optional[str] = ..., + unique_for_year: Optional[str] = ..., + choices: Optional[_FieldChoices] = ..., + help_text: str = ..., + db_column: Optional[str] = ..., + db_tablespace: Optional[str] = ..., + validators: Iterable[_ValidatorCallable] = ..., + error_messages: Optional[_ErrorMessagesToOverride] = ..., + ): ... + +class PointField(GeometryField): ... +class LineStringField(GeometryField): ... +class PolygonField(GeometryField): ... +class MultiPointField(GeometryField): ... +class MultiLineStringField(GeometryField): ... +class MultiPolygonField(GeometryField): ... +class GeometryCollectionField(GeometryField): ... +class RasterField(BaseSpatialField): ... diff --git a/django-stubs/db/models/fields/__init__.pyi b/django-stubs/db/models/fields/__init__.pyi index 8d599e6..7911488 100644 --- a/django-stubs/db/models/fields/__init__.pyi +++ b/django-stubs/db/models/fields/__init__.pyi @@ -55,9 +55,11 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]): model: Type[Model] name: str verbose_name: str + description: str blank: bool = ... null: bool = ... editable: bool = ... + empty_strings_allowed: bool = ... choices: Optional[_FieldChoices] = ... db_column: Optional[str] column: str diff --git a/test-data/typecheck/models/test_inheritance.yml b/test-data/typecheck/models/test_inheritance.yml index d31f16b..78366f4 100644 --- a/test-data/typecheck/models/test_inheritance.yml +++ b/test-data/typecheck/models/test_inheritance.yml @@ -67,4 +67,49 @@ from django.db import models class B(models.Model): b_attr = 1 - pass \ No newline at end of file + pass + + +- case: fields_recognized_if_base_model_is_subclass_of_models_model + main: | + from myapp.models import User + reveal_type(User().username) # N: Revealed type is 'builtins.str*' + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + from myapp.utils import MyBaseModel + class User(MyBaseModel): + username = models.CharField(max_length=100) + - path: myapp/utils.py + content: | + from django.db.models import Model + class MyBaseModel(Model): + pass + + +- case: django_contrib_gis_base_model_mixin_inheritance + main: | + from myapp.models import User + reveal_type(User().name) # N: Revealed type is 'builtins.str*' + reveal_type(User().updated_at) # N: Revealed type is 'datetime.datetime*' + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + from django.contrib.gis.db import models as gis_models + class Mixin1(gis_models.Model): + class Meta: + abstract = True + class Mixin2(gis_models.Model): + updated_at = models.DateTimeField(auto_now=True) + class Meta: + abstract = True + class User(Mixin1, Mixin2): + name = models.TextField() \ No newline at end of file