Рубрики
Без рубрики

Динамический атрибут, благодаря магическим методам.

Сценарий Для оптимизации нашего приложения я добавил аннотацию на моем запросе в ордина … Теги с Python, Django.

Чтобы оптимизировать наше приложение, я добавил аннотацию в моем запросе, чтобы иметь возможность фильтровать на этом динамическом свойстве и выполнять объемные операции без необходимости итерации по всем объектам в запросе:

Так что я мог бы заменить это (упрощенный пример):

class MyModel(models.Model):
    attr1 = models.DateTimeField()
    attr2 = models.IntegerField()

    def attr3(self):
        return self.attr1 + timedelta(days=attr2 * 3)

def my_function():
    for instance in MyModel.objects.all():
        # do some work involving attr3

Этим (неполный пример):

class MyManager(models.Manager):
    def get_queryset(self):
        return self.custom_annotation(super(MyManager, self).get_queryset())

    def custom_annotation(self, qs):
        return qs.annotate(
            attr3=  # Details of annotation outside the scope of this post
        )


class MyModel(models.Model):
    attr1 = models.DateTimeField()
    attr2 = models.IntegerField()

    objects = MyManager()

def my_function():
    MyModel.objects.filter(
        attr3=...  # Details of filter outside the scope of this post
    ).update(
        # Details of operation outside the scope of this post
    )

def my_other_function():
    instance = MyModel.objects.get(pk=42)
    instance.attr3  # the instance is annotated thanks to the QuerySet annotation

Хорошо, хорошо, пока Но это имеет недостаток. Когда вы получаете доступ к вашей модели напрямую (через Queryset или экземпляр), он аннотируется. Когда вы получаете доступ к нему через Manytomany это тоже аннотируется Но не когда он используется в качестве Иностранник :

class MyModel2(models.Model):
    foreign = models.ForeignKey(MyModel)

def my_function():
    instance2 = MyModel2.objects.get(pk=57)
    instance2.foreign.attr3  # AttributeError

Так что я хотел иметь MyModel Действуйте динамически и иди получите attr3 При необходимости при необходимости атрибут при необходимости затраты дополнительного запроса SQL.

Это возможно, переопределение __getattr__ как это:

class MyModel(models.Model):
    attr1 = models.DateTimeField()
    attr2 = models.IntegerField()

    objects = MyManager()

    def __getattr__(self, attrname):
        if attrname == "attr3":
            try:
                return object.__getattribute__(self, attrname)
            except AttributeError:
                value = Screen.objects.values_list("attr3", flat=True).get(pk=self.pk)
                setattr(self, attrname, value)
                return value
        return object.__getattribute__(self, attrname)

Один должен быть осторожен при переопределении __getattr__ , вы можете легко попасть в бесконечные петли.

Кроме того, важно отметить, что __getattr__ Только вызывается при доступе к отсутствующему атрибуту, тогда как __getattribute__ Вызывается каждый раз, когда доступ к атрибуту.

Я использую объект .__ getattribute__ во избежание рекурсии.

Фото Clem Onojeghuo на Unsplash

Оригинал: “https://dev.to/courtjus/dynamic-attribute-thanks-to-super-methods-3igl”