Skip to content

Commit 1ce442c

Browse files
Refactor get_field_related_model_cls to raise UnregisteredModelError
1 parent f6457d1 commit 1ce442c

File tree

5 files changed

+38
-22
lines changed

5 files changed

+38
-22
lines changed

mypy_django_plugin/django/context.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from mypy.types import AnyType, Instance, TypeOfAny, UnionType
2121
from mypy.types import Type as MypyType
2222

23+
from mypy_django_plugin.exceptions import UnregisteredModelError
2324
from mypy_django_plugin.lib import fullnames, helpers
2425
from mypy_django_plugin.lib.fullnames import WITH_ANNOTATIONS_FULLNAME
2526

@@ -123,7 +124,14 @@ def get_model_foreign_keys(self, model_cls: Type[Model]) -> Iterator["ForeignKey
123124
if isinstance(field, ForeignKey):
124125
yield field
125126

127+
def get_model_related_fields(self, model_cls: Type[Model]) -> Iterator["RelatedField[Any, Any]"]:
128+
"""Get model forward relations"""
129+
for field in model_cls._meta.get_fields():
130+
if isinstance(field, RelatedField):
131+
yield field
132+
126133
def get_model_relations(self, model_cls: Type[Model]) -> Iterator[ForeignObjectRel]:
134+
"""Get model reverse relations"""
127135
for field in model_cls._meta.get_fields():
128136
if isinstance(field, ForeignObjectRel):
129137
yield field
@@ -334,9 +342,7 @@ def get_field_get_type(
334342
else:
335343
return helpers.get_private_descriptor_type(field_info, "_pyi_private_get_type", is_nullable=is_nullable)
336344

337-
def get_field_related_model_cls(
338-
self, field: Union["RelatedField[Any, Any]", ForeignObjectRel]
339-
) -> Optional[Type[Model]]:
345+
def get_field_related_model_cls(self, field: Union["RelatedField[Any, Any]", ForeignObjectRel]) -> Type[Model]:
340346
if isinstance(field, RelatedField):
341347
related_model_cls = field.remote_field.model
342348
else:
@@ -350,11 +356,13 @@ def get_field_related_model_cls(
350356
# same file model
351357
related_model_fullname = field.model.__module__ + "." + related_model_cls
352358
related_model_cls = self.get_model_class_by_fullname(related_model_fullname)
359+
if related_model_cls is None:
360+
raise UnregisteredModelError
353361
else:
354362
try:
355363
related_model_cls = self.apps_registry.get_model(related_model_cls)
356-
except LookupError:
357-
return None
364+
except LookupError as e:
365+
raise UnregisteredModelError from e
358366

359367
return related_model_cls
360368

@@ -442,6 +450,8 @@ def resolve_lookup_expected_type(self, ctx: MethodContext, model_cls: Type[Model
442450
except FieldError as exc:
443451
ctx.api.fail(exc.args[0], ctx.context)
444452
return AnyType(TypeOfAny.from_error)
453+
except UnregisteredModelError:
454+
return AnyType(TypeOfAny.from_error)
445455

446456
if solved_lookup is None:
447457
return AnyType(TypeOfAny.implementation_artifact)

mypy_django_plugin/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class UnregisteredModelError(Exception):
2+
"""The requested model is not registered"""

mypy_django_plugin/main.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import itertools
12
import sys
23
from functools import partial
34
from typing import Callable, Dict, List, Optional, Tuple, Type
45

5-
from django.db.models.fields.related import RelatedField
66
from mypy.modulefinder import mypy_path
77
from mypy.nodes import MypyFile, TypeInfo
88
from mypy.options import Options
@@ -20,6 +20,7 @@
2020
import mypy_django_plugin.transformers.orm_lookups
2121
from mypy_django_plugin.config import DjangoPluginConfig
2222
from mypy_django_plugin.django.context import DjangoContext
23+
from mypy_django_plugin.exceptions import UnregisteredModelError
2324
from mypy_django_plugin.lib import fullnames, helpers
2425
from mypy_django_plugin.transformers import fields, forms, init_create, meta, querysets, request, settings
2526
from mypy_django_plugin.transformers.functional import resolve_str_promise_attribute
@@ -147,23 +148,22 @@ def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
147148
if not defined_model_classes:
148149
return []
149150
deps = set()
151+
150152
for model_class in defined_model_classes:
151-
# forward relations
152-
for field in self.django_context.get_model_fields(model_class):
153-
if isinstance(field, RelatedField):
153+
for field in itertools.chain(
154+
# forward relations
155+
self.django_context.get_model_related_fields(model_class),
156+
# reverse relations - `related_objects` is private API (according to docstring)
157+
model_class._meta.related_objects, # type: ignore[attr-defined]
158+
):
159+
try:
154160
related_model_cls = self.django_context.get_field_related_model_cls(field)
155-
if related_model_cls is None:
156-
continue
157-
related_model_module = related_model_cls.__module__
158-
if related_model_module != file.fullname:
159-
deps.add(self._new_dependency(related_model_module))
160-
# reverse relations
161-
# `related_objects` is private API (according to docstring)
162-
for relation in model_class._meta.related_objects: # type: ignore[attr-defined]
163-
related_model_cls = self.django_context.get_field_related_model_cls(relation)
161+
except UnregisteredModelError:
162+
continue
164163
related_model_module = related_model_cls.__module__
165164
if related_model_module != file.fullname:
166165
deps.add(self._new_dependency(related_model_module))
166+
167167
return list(deps) + [
168168
# for QuerySet.annotate
169169
self._new_dependency("django_stubs_ext"),

mypy_django_plugin/transformers/fields.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from mypy.types import Type as MypyType
1111

1212
from mypy_django_plugin.django.context import DjangoContext
13+
from mypy_django_plugin.exceptions import UnregisteredModelError
1314
from mypy_django_plugin.lib import fullnames, helpers
1415

1516
if TYPE_CHECKING:
@@ -59,8 +60,9 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
5960

6061
assert isinstance(current_field, RelatedField)
6162

62-
related_model_cls = django_context.get_field_related_model_cls(current_field)
63-
if related_model_cls is None:
63+
try:
64+
related_model_cls = django_context.get_field_related_model_cls(current_field)
65+
except UnregisteredModelError:
6466
return AnyType(TypeOfAny.from_error)
6567

6668
default_related_field_type = set_descriptor_types_for_field(ctx)

mypy_django_plugin/transformers/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from mypy_django_plugin.django.context import DjangoContext
1616
from mypy_django_plugin.errorcodes import MANAGER_MISSING
17+
from mypy_django_plugin.exceptions import UnregisteredModelError
1718
from mypy_django_plugin.lib import fullnames, helpers
1819
from mypy_django_plugin.lib.fullnames import ANNOTATIONS_FULLNAME, ANY_ATTR_ALLOWED_CLASS_FULLNAME, MODEL_CLASS_FULLNAME
1920
from mypy_django_plugin.transformers import fields
@@ -234,8 +235,9 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
234235
class AddRelatedModelsId(ModelClassInitializer):
235236
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
236237
for field in self.django_context.get_model_foreign_keys(model_cls):
237-
related_model_cls = self.django_context.get_field_related_model_cls(field)
238-
if related_model_cls is None:
238+
try:
239+
related_model_cls = self.django_context.get_field_related_model_cls(field)
240+
except UnregisteredModelError:
239241
error_context: Context = self.ctx.cls
240242
field_sym = self.ctx.cls.info.get(field.name)
241243
if field_sym is not None and field_sym.node is not None:

0 commit comments

Comments
 (0)