Example🔗

Lazy managers🔗

Given the following models:

from django.db import models

class Project(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

class Task(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tasks")

We can delay the evaluation of a manager with the LazyModelManager descriptor:

from typing import ClassVar, TYPE_CHECKING
from django.db import models
from lazy_managers import LazyModelManager

if TYPE_CHECKING:
    from .querysets import ProjectManager, TaskManager


class Project(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    objects: ClassVar[ProjectManager] = LazyModelManager.new()

class Task(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tasks")

    objects: ClassVar[TaskManager] = LazyModelManager.new()

With this, our manager (and queryset) classes will be lazily loaded when it is first accessed, either from the class itself, or from a to-many relationship.

The advantage of this is that our manager (and queryset) module can freely import other modules without causing cyclical imports.

Lazy attributes🔗

The library also provides a way to lazily load other attributes on a model, with the same benefits of avoiding cyclical imports.

from typing import ClassVar, TYPE_CHECKING
from django.db import models
from lazy_managers import LazyModelAttribute

if TYPE_CHECKING:
    from .validators import ProjectValidators, TaskValidators


class Project(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    validators: ClassVar[ProjectValidators] = LazyModelAttribute.new()

class Task(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tasks")

    validators: ClassVar[TaskValidators] = LazyModelAttribute.new()

Here the attribute should take a single argument, which is the instance of the model being accessed. However, the attribute can be accessed on the class level, in which case the attribute class itself will be given from the descriptor.