VS Code version
1.126.04524
OS
Ubuntu 24.04 (Tuxedo OS)
Steps to reproduce
DOL007 fires on a plain class-attribute access when the loop iterates over model classes (as returned by apps.get_models()), reporting an N+1 and suggesting select_related() / prefetch_related(). No query is involved.
An abstract base declares two retention periods as plain class attributes:
class Auditory(models.Model):
ANONYMISE_AFTER: ClassVar[dt.timedelta | None] = None
PURGE_AFTER: ClassVar[dt.timedelta | None] = None
class Meta:
abstract = True
A helper collects the concrete models that inherit it:
def auditory_models() -> list[type[Auditory]]:
"""Every concrete model inheriting Auditory."""
return [
model
for model in apps.get_models()
if issubclass(model, Auditory) and not model._meta.abstract
]
A sweep visits each model and reads its period:
for model in auditory_models():
if model.ANONYMISE_AFTER is None: # <-- DOL007 reported here
continue
...
Reported message:
Attribute access 'model.ANONYMISE_AFTER' inside a loop over 'auditory_models()' - consider .select_related() or .prefetch_related() to avoid an N+1.
Notes:
apps.get_models() returns model classes (ModelBase instances), not model instances, so model.ANONYMISE_AFTER is an in-memory __mro__ lookup on a class object.
ANONYMISE_AFTER is not a field and not a relation, so select_related() and prefetch_related() have nothing to act on.
This is the bit of code used to verify it:
with CaptureQueriesContext(connection) as captured:
for model in auditory_models():
_ = model.ANONYMISE_AFTER
assert len(captured.captured_queries) == 0
Sample models.py (minimized)
VS Code version
1.126.04524
OS
Ubuntu 24.04 (Tuxedo OS)
Steps to reproduce
DOL007fires on a plain class-attribute access when the loop iterates over model classes (as returned byapps.get_models()), reporting an N+1 and suggestingselect_related()/prefetch_related(). No query is involved.An abstract base declares two retention periods as plain class attributes:
A sweep visits each model and reads its period:
Reported message:
Notes:
apps.get_models()returns model classes (ModelBaseinstances), not model instances, somodel.ANONYMISE_AFTERis an in-memory__mro__lookup on a class object.ANONYMISE_AFTERis not a field and not a relation, soselect_related()andprefetch_related()have nothing to act on.This is the bit of code used to verify it:
Sample models.py (minimized)