Skip to content

Commit e933c5e

Browse files
Handle possible unregistered models
1 parent d5ad93e commit e933c5e

File tree

2 files changed

+24
-4
lines changed

2 files changed

+24
-4
lines changed

mypy_django_plugin/django/context.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ def get_field_lookup_exact_type(
128128
) -> MypyType:
129129
if isinstance(field, (RelatedField, ForeignObjectRel)):
130130
related_model_cls = self.get_field_related_model_cls(field)
131+
if related_model_cls is None:
132+
return AnyType(TypeOfAny.from_error)
131133
primary_key_field = self.get_primary_key_field(related_model_cls)
132134
primary_key_type = self.get_field_get_type(api, primary_key_field, method="init")
133135

@@ -353,7 +355,7 @@ def get_field_related_model_cls(
353355

354356
def _resolve_field_from_parts(
355357
self, field_parts: Iterable[str], model_cls: Type[Model]
356-
) -> Union["Field[Any, Any]", ForeignObjectRel]:
358+
) -> Union["Field[Any, Any]", ForeignObjectRel, None]:
357359
currently_observed_model = model_cls
358360
field: Union["Field[Any, Any]", ForeignObjectRel, GenericForeignKey, None] = None
359361
for field_part in field_parts:
@@ -363,13 +365,19 @@ def _resolve_field_from_parts(
363365

364366
field = currently_observed_model._meta.get_field(field_part)
365367
if isinstance(field, RelatedField):
366-
currently_observed_model = self.get_field_related_model_cls(field)
368+
related_model = self.get_field_related_model_cls(field)
369+
if related_model is None:
370+
return None
371+
currently_observed_model = related_model
367372
model_name = currently_observed_model._meta.model_name
368373
if model_name is not None and field_part == (model_name + "_id"):
369374
field = self.get_primary_key_field(currently_observed_model)
370375

371376
if isinstance(field, ForeignObjectRel):
372-
currently_observed_model = self.get_field_related_model_cls(field)
377+
related_model = self.get_field_related_model_cls(field)
378+
if related_model is None:
379+
return None
380+
currently_observed_model = related_model
373381

374382
# Guaranteed by `query.solve_lookup_type` before.
375383
assert isinstance(field, (Field, ForeignObjectRel))
@@ -397,9 +405,18 @@ def solve_lookup_type(
397405
field = query.get_meta().get_field(query_parts[0])
398406
except FieldDoesNotExist:
399407
return None
408+
400409
if len(query_parts) == 1:
401410
return [], [query_parts[0]], False
402-
sub_query = Query(field.related_model).solve_lookup_type("__")
411+
412+
related_model = None
413+
if isinstance(field, (RelatedField, ForeignObjectRel)):
414+
related_model = self.get_field_related_model_cls(field)
415+
416+
if related_model is None:
417+
return None
418+
419+
sub_query = Query(related_model).solve_lookup_type("__".join(query_parts[1:]))
403420
entire_query_parts = [query_parts[0], *sub_query[1]]
404421
return sub_query[0], entire_query_parts, sub_query[2]
405422

@@ -428,6 +445,8 @@ def resolve_lookup_expected_type(self, ctx: MethodContext, model_cls: Type[Model
428445
return AnyType(TypeOfAny.explicit)
429446

430447
field = self._resolve_field_from_parts(field_parts, model_cls)
448+
if field is None:
449+
return AnyType(TypeOfAny.from_error)
431450

432451
lookup_cls = None
433452
if lookup_parts:

tests/typecheck/fields/test_related.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,7 @@
938938
- case: test_fails_if_app_label_is_unknown_in_relation_field
939939
main: |
940940
from installed.models import InstalledModel
941+
InstalledModel.objects.filter(non_installed__isnull=True)
941942
installed_apps:
942943
- installed
943944
files:

0 commit comments

Comments
 (0)