mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-06 20:24:31 +08:00
Fix crash if model from same app referenced in RelatedField cannot be resolved (#199)
* do not crash if model from same app refd in ForeignKey cannot be resolved * bump to 1.2.0
This commit is contained in:
@@ -177,6 +177,10 @@ class DjangoContext:
|
||||
continue
|
||||
|
||||
related_model = self.get_field_related_model_cls(field)
|
||||
if related_model is None:
|
||||
expected_types[field_name] = AnyType(TypeOfAny.from_error)
|
||||
continue
|
||||
|
||||
if related_model._meta.proxy_for_model is not None:
|
||||
related_model = related_model._meta.proxy_for_model
|
||||
|
||||
@@ -260,6 +264,8 @@ class DjangoContext:
|
||||
is_nullable = self.get_field_nullability(field, method)
|
||||
if isinstance(field, RelatedField):
|
||||
related_model_cls = self.get_field_related_model_cls(field)
|
||||
if related_model_cls is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
|
||||
if method == 'values':
|
||||
primary_key_field = self.get_primary_key_field(related_model_cls)
|
||||
@@ -274,7 +280,7 @@ class DjangoContext:
|
||||
return helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
|
||||
is_nullable=is_nullable)
|
||||
|
||||
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Type[Model]:
|
||||
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Optional[Type[Model]]:
|
||||
if isinstance(field, RelatedField):
|
||||
related_model_cls = field.remote_field.model
|
||||
else:
|
||||
|
||||
@@ -150,6 +150,8 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
for field in self.django_context.get_model_fields(model_class):
|
||||
if isinstance(field, RelatedField):
|
||||
related_model_cls = self.django_context.get_field_related_model_cls(field)
|
||||
if related_model_cls is None:
|
||||
continue
|
||||
related_model_module = related_model_cls.__module__
|
||||
if related_model_module != file.fullname():
|
||||
deps.add(self._new_dependency(related_model_module))
|
||||
|
||||
@@ -45,6 +45,8 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
|
||||
assert isinstance(current_field, RelatedField)
|
||||
|
||||
related_model_cls = django_context.get_field_related_model_cls(current_field)
|
||||
if related_model_cls is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
|
||||
related_model = related_model_cls
|
||||
related_model_to_set = related_model_cls
|
||||
|
||||
@@ -8,11 +8,13 @@ from django.db.models.fields.reverse_related import (
|
||||
ManyToManyRel, ManyToOneRel, OneToOneRel,
|
||||
)
|
||||
from mypy.nodes import (
|
||||
ARG_STAR2, MDEF, Argument, SymbolTableNode, TypeInfo, Var,
|
||||
ARG_STAR2, MDEF, Argument, Context, SymbolTableNode, TypeInfo, Var,
|
||||
)
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.plugins import common
|
||||
from mypy.types import AnyType, Instance, TypeOfAny
|
||||
from mypy.types import AnyType, Instance
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypeOfAny
|
||||
|
||||
from mypy_django_plugin.django.context import DjangoContext
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
@@ -38,7 +40,7 @@ class ModelClassInitializer:
|
||||
field_info = self.lookup_typeinfo_or_incomplete_defn_error(fullname)
|
||||
return field_info
|
||||
|
||||
def create_new_var(self, name: str, typ: Instance) -> Var:
|
||||
def create_new_var(self, name: str, typ: MypyType) -> Var:
|
||||
# type=: type of the variable itself
|
||||
var = Var(name=name, type=typ)
|
||||
# var.info: type of the object variable is bound to
|
||||
@@ -48,7 +50,7 @@ class ModelClassInitializer:
|
||||
var.is_inferred = True
|
||||
return var
|
||||
|
||||
def add_new_node_to_model_class(self, name: str, typ: Instance) -> None:
|
||||
def add_new_node_to_model_class(self, name: str, typ: MypyType) -> None:
|
||||
var = self.create_new_var(name, typ)
|
||||
self.model_classdef.info.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||
|
||||
@@ -100,6 +102,18 @@ class AddRelatedModelsId(ModelClassInitializer):
|
||||
for field in model_cls._meta.get_fields():
|
||||
if isinstance(field, ForeignKey):
|
||||
related_model_cls = self.django_context.get_field_related_model_cls(field)
|
||||
if related_model_cls is None:
|
||||
error_context: Context = self.ctx.cls
|
||||
field_sym = self.ctx.cls.info.get(field.name)
|
||||
if field_sym is not None and field_sym.node is not None:
|
||||
error_context = field_sym.node
|
||||
self.api.fail(f'Cannot find model {field.related_model!r} '
|
||||
f'referenced in field {field.name!r} ',
|
||||
ctx=error_context)
|
||||
self.add_new_node_to_model_class(field.attname,
|
||||
AnyType(TypeOfAny.explicit))
|
||||
continue
|
||||
|
||||
rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
|
||||
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
|
||||
is_nullable = self.django_context.get_field_nullability(field, None)
|
||||
@@ -163,8 +177,10 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
continue
|
||||
|
||||
related_model_cls = self.django_context.get_field_related_model_cls(relation)
|
||||
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
|
||||
if related_model_cls is None:
|
||||
continue
|
||||
|
||||
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
|
||||
if isinstance(relation, OneToOneRel):
|
||||
self.add_new_node_to_model_class(attname, Instance(related_model_info, []))
|
||||
continue
|
||||
|
||||
@@ -49,6 +49,8 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
|
||||
|
||||
if isinstance(lookup_field, RelatedField) and lookup_field.column == lookup:
|
||||
related_model_cls = django_context.get_field_related_model_cls(lookup_field)
|
||||
if related_model_cls is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
lookup_field = django_context.get_primary_key_field(related_model_cls)
|
||||
|
||||
field_get_type = django_context.get_field_get_type(helpers.get_typechecker_api(ctx),
|
||||
|
||||
4
setup.py
4
setup.py
@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
|
||||
readme = f.read()
|
||||
|
||||
dependencies = [
|
||||
'mypy>=0.730,<0.740',
|
||||
'mypy>=0.730',
|
||||
'typing-extensions',
|
||||
'django',
|
||||
]
|
||||
|
||||
setup(
|
||||
name="django-stubs",
|
||||
version="1.1.0",
|
||||
version="1.2.0",
|
||||
description='Mypy stubs for Django',
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
|
||||
@@ -387,22 +387,52 @@
|
||||
class Book2(models.Model):
|
||||
publisher = models.ForeignKey(to=Publisher2, on_delete=models.CASCADE)
|
||||
|
||||
- case: if_model_is_defined_as_name_of_the_class_look_for_it_in_the_same_file
|
||||
- case: if_model_is_defined_as_name_of_the_class_look_for_it_in_the_same_app
|
||||
main: |
|
||||
from myapp.models import Book
|
||||
reveal_type(Book().publisher) # N: Revealed type is 'myapp.models.Publisher*'
|
||||
reveal_type(Book().publisher) # N: Revealed type is 'myapp.models.publisher.Publisher*'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
- path: myapp/models/__init__.py
|
||||
content: |
|
||||
from .publisher import Publisher
|
||||
from .book import Book
|
||||
- path: myapp/models/publisher.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Publisher(models.Model):
|
||||
pass
|
||||
- path: myapp/models/book.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Book(models.Model):
|
||||
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
|
||||
|
||||
|
||||
- case: fail_if_no_model_in_the_same_app_models_init_py
|
||||
main: |
|
||||
from myapp.models import Book
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models/__init__.py
|
||||
content: |
|
||||
from .book import Book
|
||||
- path: myapp/models/publisher.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Publisher(models.Model):
|
||||
pass
|
||||
- path: myapp/models/book.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Book(models.Model):
|
||||
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE) # E: Cannot find model 'Publisher' referenced in field 'publisher'
|
||||
|
||||
|
||||
- case: test_foreign_key_field_without_backwards_relation
|
||||
main: |
|
||||
from myapp.models import Book, Publisher
|
||||
|
||||
Reference in New Issue
Block a user