← Назад к вопросам

Как используется двойное подчеркивание в названии методов в Django?

2.0 Middle🔥 101 комментариев
#Django#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Двойное подчеркивание в названии методов в Django

Вопрос о двойном подчеркивании (__) в Python часто путают с конкретикой Django. На самом деле это фундаментальный механизм Python, который Django тоже использует.

Что такое двойное подчеркивание в Python

Двойное подчеркивание (dunder) запускает name mangling — это механизм приватности на уровне синтаксиса.

class User:
    def __init__(self):
        self.__password = "secret"
    
    def __validate(self):
        pass

user = User()
# Это не работает: print(user.__password)
# Но Python переименовывает его:
print(user._User__password)  # Работает!

На самом деле Python просто переименовывает __password в _ClassName__password. Это сигнал: "Это не для использования извне".

Использование в Django

1. Защита от перезаписи в дочерних классах:

from django.db import models

class BaseModel(models.Model):
    __created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        abstract = True

class Article(BaseModel):
    # Если написать __created_at, не перезапишет BaseModel
    pass

2. Приватные утилиты в моделях:

from django.db import models

class Order(models.Model):
    total_price = models.DecimalField(max_digits=10, decimal_places=2)
    
    def __calculate_tax(self):
        return self.total_price * Decimal('0.18')
    
    def get_total_with_tax(self):
        return self.total_price + self.__calculate_tax()

Снаружи вызываете get_total_with_tax(), но не __calculate_tax().

Правда про Django и приватность

На практике Django разработчики редко используют __ потому что:

  1. Код в Django часто наследуется: если вы используете __method, дочерний класс не сможет легко обратиться к нему
  2. Тестирование усложняется: тестам часто нужно вызывать приватные методы
  3. Зачем скрывать, если есть соглашение: в Python используют _ для приватности

Рекомендуемый подход в Django

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    
    # Правильно: один подчеркивание = приватное, но наследуемое
    def _calculate_discount(self, percent: float):
        return self.price * Decimal(str(percent / 100))
    
    def get_discounted_price(self, discount_percent: float):
        return self.price - self._calculate_discount(discount_percent)

class SpecialProduct(Product):
    def _calculate_discount(self, percent: float):
        return super()._calculate_discount(percent) * 2

Когда НЕ использовать __ в Django

# Антипаттерн
class Article(models.Model):
    __slug = models.SlugField()  # Плохо!
    __content = models.TextField()  # Плохо!

Проблема: дочерний класс не сможет легко получить доступ.

Исключения: Когда всё же использовать __

В служебных классах без наследования:

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    def __handle_error(self):
        pass
    
    def handle(self, *args, **options):
        self.__handle_error()

Практический пример из реального проекта

from django.db import models
from decimal import Decimal

class Payment(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20)
    
    def _validate_amount(self) -> bool:
        return self.amount > 0
    
    def _log_transaction(self, message: str):
        logger.info(f"Payment {self.id}: {message}")
    
    def process(self) -> bool:
        if not self._validate_amount():
            self._log_transaction("Validation failed")
            return False
        
        if self.charge_card():
            self._log_transaction("Payment successful")
            self.status = 'completed'
            self.save()
            return True
        
        self._log_transaction("Payment failed")
        return False
    
    def charge_card(self) -> bool:
        pass

Итоговые правила для Django

  • Используй _method (один подчеркивание) для приватных методов — это соглашение Python
  • Используй __method (двойное) только если создаёшь базовый класс БЕЗ наследования
  • Не используй __ для полей модели — это никогда не делается
  • Тестируй приватные методы через публичное API
  • Документируй, почему метод приватный

Помни: Python это не Java. Приватность это соглашение, а не закон.