Чтобы оптимизировать наше приложение, я добавил аннотацию в моем запросе, чтобы иметь возможность фильтровать на этом динамическом свойстве и выполнять объемные операции без необходимости итерации по всем объектам в запросе:
Так что я мог бы заменить это (упрощенный пример):
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__
во избежание рекурсии.
- Документация Python: https://docs.ython.org/3/reference/datamodel.html.
- Обсуждение о разнице между обоими магическими методами: https://stackoverflow.com/questions/3278077/difference-vetween-getttr-vs-getattribute.
Фото Clem Onojeghuo на Unsplash
Оригинал: “https://dev.to/courtjus/dynamic-attribute-thanks-to-super-methods-3igl”