Декоратор classproperty

Декоратор classproperty из модуля django.utils.decorators позволяет определить метод класса как свойство, которое можно вызывать без создания экземпляра класса. В отличие от обычного property, который работает с экземплярами, classproperty работает на уровне класса и принимает первым аргументом ссылку на класс, а не на экземпляр.

Этот декоратор особенно полезен для создания вычисляемых атрибутов, которые зависят только от структуры класса или данных, общих для всех экземпляров. Например, можно создать свойство, которое возвращает количество записей в модели или строковое представление имени класса.

Синтаксис

pythonfrom django.utils.decorators import classproperty

class MyClass:
  @classproperty
  def my_property(cls):
    return "value"

Декоратор classproperty применяется к методу класса, который принимает первым аргументом ссылку на класс (обычно называется cls) и возвращает вычисляемое значение. Обращение к такому свойству происходит через имя класса, как к обычному атрибуту, без вызова метода.

Пример . С моделью Django

Рассмотрим пример создания свойства класса для модели Article, которое возвращает количество опубликованных статей:

pythonfrom django.db import models
from django.utils.decorators import classproperty

class Article(models.Model):
  title = models.CharField(max_length=100)
  status = models.CharField(max_length=20)
  
  @classproperty
  def published_count(cls):
    return cls.objects.filter(status='published').count()

Теперь свойство можно использовать без создания экземпляра класса:

pythonfrom myapp.models import Article

count = Article.published_count
print(count)

Результат выполнения кода:

python"42"

Пример . Вычисляемое свойство

Создадим класс, который использует classproperty для динамического формирования имени таблицы:

pythonfrom django.db import models
from django.utils.decorators import classproperty

class BaseModel(models.Model):
  class Meta:
    abstract = True
  
  @classproperty
  def table_name(cls):
    return f"app_{cls.__name__.lower()}"
  
  @classproperty
  def verbose_name_plural(cls):
    return f"{cls.__name__}s"

Пример использования свойств класса для получения имени таблицы и множественного числа названия:

pythonfrom myapp.models import BaseModel

class Product(BaseModel):
  name = models.CharField(max_length=100)

print(Product.table_name)
print(Product.verbose_name_plural)

Результат выполнения кода:

python"app_product"
"Products"

Отличие от cached_property

В отличие от декоратора cached_property, который кеширует результат для экземпляра, classproperty вычисляет значение при каждом обращении к свойству класса. Если требуется кеширование на уровне класса, нужно использовать cached_property в сочетании с дополнительной логикой или использовать ручное кеширование.

pythonfrom django.db import models
from django.utils.decorators import classproperty

class Category(models.Model):
  name = models.CharField(max_length=50)
  
  @classproperty
  def all_names(cls):
    return [c.name for c in cls.objects.all()]

При каждом обращении к Category.all_names будет выполняться запрос к базе данных. Для кеширования результата можно использовать ручное кеширование или специальные декораторы.

Смотрите также

  • декоратор cached_property,
    который кеширует результат свойства для экземпляра
  • декоратор method_decorator,
    который применяет декоратор к методам класса
  • функция lazy,
    которая создает ленивые вычисления в Django
  • функция mark_safe,
    которая помечает строку как безопасную для вывода