Ответ
Дескриптор — это протокол в Python, который позволяет объекту настраивать поведение при доступе к атрибутам другого объекта. Объект является дескриптором, если он реализует один или несколько "магических" методов: __get__, __set__ или __delete__.
Django активно использует этот механизм для реализации полей моделей (models.Field). Каждое поле в модели Django (например, CharField, IntegerField) является дескриптором.
Почему это важно для Django? Дескрипторы позволяют полям модели:
- Преобразовывать типы данных: значение из базы данных (например, строка) автоматически преобразуется в нужный тип Python (например,
datetime.date) при чтении (__get__). - Выполнять валидацию: при присваивании значения полю (
__set__) можно проверить его на соответствие правилам (например,max_length). - Реализовывать ленивую загрузку: связанные объекты (
ForeignKey) не загружаются из базы данных до тех пор, пока к ним не обратятся (__get__).
Простой пример дескриптора (чистый Python):
Этот дескриптор проверяет, что присваиваемое значение является положительным числом.
class PositiveNumber:
def __set_name__(self, owner, name):
# Сохраняем имя атрибута, к которому привязан дескриптор
self.public_name = name
self.private_name = '_' + name
def __get__(self, obj, objtype=None):
# Получаем значение из приватного атрибута экземпляра
return getattr(obj, self.private_name)
def __set__(self, obj, value):
# Проверяем значение перед присваиванием
if not isinstance(value, (int, float)) or value <= 0:
raise ValueError(f"'{self.public_name}' must be a positive number.")
setattr(obj, self.private_name, value)
class Product:
price = PositiveNumber()
quantity = PositiveNumber()
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
# Использование
product = Product(100.5, 10)
print(product.price) # Вывод: 100.5
try:
product.price = -50 # Вызовет ValueError
except ValueError as e:
print(e) # Вывод: 'price' must be a positive number.
Как это выглядит в Django:
Когда вы объявляете поле title = models.CharField(max_length=100), вы создаете экземпляр дескриптора CharField. Django при инициализации модели связывает этот дескриптор с атрибутом title. Присваивание my_article.title = "..." вызывает метод __set__ дескриптора, который выполняет проверку длины.