Skip to content

Commit

Permalink
Fallback to inspect.getmodule for models not exported in the app's …
Browse files Browse the repository at this point in the history
…models module (#66)
  • Loading branch information
Viicos authored May 29, 2024
1 parent 35a984b commit 3959bba
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/django_autotyping/stubbing/django_context/django_context.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import inspect
from collections import defaultdict
from types import ModuleType

from django.apps.registry import Apps
from django.conf import LazySettings
Expand All @@ -25,12 +27,26 @@ def __init__(self, apps: Apps, settings: LazySettings) -> None:

@staticmethod
def _get_model_alias(model: ModelType) -> str:
"""Return an alias of the model, by converting the app label to PascalCase and joining
"""Return an alias of the model.
The alias is constructed by converting the app label to PascalCase and joining
the app label to the model name.
"""
app_label = to_pascal(model._meta.app_label)
return f"{app_label}{model.__name__}"

@staticmethod
def _get_model_module(model: ModelType) -> ModuleType:
"""Return the module object where the model class is exported or defined.
Use the models module of the app where the model is defined, and fallback
to the actual module of the model class in case the model is not exported
or present in the models module.
"""
if model._meta.app_config.models_module is not None:
return model._meta.app_config.models_module
return inspect.getmodule(model) # type: ignore

@property
def models(self) -> list[ModelType]:
"""All the defined models. Abstract models are not included."""
Expand All @@ -45,7 +61,7 @@ def model_imports(self) -> list[ImportItem]:

return [
ImportItem(
module_name=model._meta.app_config.models_module.__name__,
module_name=self._get_model_module(model).__name__,
obj_name=model.__name__,
alias=self._get_model_alias(model) if self.is_duplicate(model) else None,
)
Expand Down
Empty file.
10 changes: 10 additions & 0 deletions tests/stubstestproj/appwithoutmodelsmodule/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.apps import AppConfig


class AppwithoutmodelsmoduleConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "stubstestproj.appwithoutmodelsmodule"

def ready(self) -> None:
# See https://github.com/Viicos/django-autotyping/issues/59 for more context:
from .extra_models import ExtraModel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.db import models


class ExtraModel(models.Model):
# `ModelBase.__new__` registers every model to the default `Apps` class,
# even if the model is not defined/exported in the `AppConfig`'s models module.
# This can lead to cases where `AppConfig.models_module` is `None` because
# no models module exists, however models are still registered under this
# specific `AppConfig`.
# See https://github.com/Viicos/django-autotyping/issues/59 as an example.
pass
1 change: 1 addition & 0 deletions tests/stubstestproj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"stubstestproj.accounts",
"stubstestproj.firstapp",
"stubstestproj.secondapp",
"stubstestproj.appwithoutmodelsmodule",
]

MIDDLEWARE = [
Expand Down

0 comments on commit 3959bba

Please sign in to comment.