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

Зачем нужна прокси модель в Django?

2.0 Middle🔥 91 комментариев
#Django

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

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

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

Proxy модель в Django — одна таблица, несколько интерфейсов

Proxy модель позволяет создать несколько моделей, указывающих на одну таблицу в БД, но с разными методами и поведением. Это решение задач без дублирования данных.

Проблема без proxy

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    role = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'person'

Если нужны разные методы для Admins vs Users:

# ❌ Неправильно — создаём лишнюю таблицу
class User(models.Model):
    person_ptr = models.OneToOneField(Person, on_delete=models.CASCADE, parent_link=True)
    
    class Meta:
        db_table = 'user'  # Новая таблица!

Это создаст person и user таблицы с joins.

Решение: Proxy модель

class Person(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    role = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'person'

# ОДНА таблица — person
class Admin(Person):
    class Meta:
        proxy = True  # ← Ключевая строка!
    
    def grant_permission(self, permission):
        # Специальный метод только для админов
        self.permissions.add(permission)
    
    def deactivate_user(self, user_id):
        # Специальный метод для администрирования
        User.objects.filter(id=user_id).update(is_active=False)

class RegularUser(Person):
    class Meta:
        proxy = True
    
    def get_posts(self):
        # Специальный метод для пользователей
        return Post.objects.filter(author=self)

В БД всё ещё одна таблица person!

Практический пример

# models.py
class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

# Proxy для администраторов
class Administrator(User):
    class Meta:
        proxy = True
    
    def __str__(self):
        return f"Admin: {self.name}"
    
    def has_permission(self, perm):
        return True  # Админы имеют все прав

# Proxy для обычных пользователей
class RegularUser(User):
    class Meta:
        proxy = True
    
    def __str__(self):
        return f"User: {self.name}"
    
    def has_permission(self, perm):
        return False  # Обычные пользователи без прав

# Использование
admin = Administrator.objects.create(name="Alice", is_staff=True)
user = RegularUser.objects.create(name="Bob", is_staff=False)

print(admin.has_permission("delete_user"))  # True
print(user.has_permission("delete_user"))   # False

# Оба находятся в таблице user!
print(User.objects.count())  # 2
print(Administrator.objects.count())  # 1
print(RegularUser.objects.count())  # 1

Применение в Django Admin

from django.contrib import admin
from django.contrib.admin import ModelAdmin

# Одна модель — несколько Admin интерфейсов
class AdminAdmin(admin.ModelAdmin):
    list_display = ['name', 'email', 'permissions']
    actions = ['grant_admin_rights', 'revoke_admin_rights']
    
    def grant_admin_rights(self, request, queryset):
        queryset.update(is_staff=True)

class RegularUserAdmin(admin.ModelAdmin):
    list_display = ['name', 'email', 'created_at']
    readonly_fields = ['created_at']  # Обычные пользователи видят только чтение

admin.site.register(Administrator, AdminAdmin)
admin.site.register(RegularUser, RegularUserAdmin)

В Django Admin они будут в разных списках!

Запросы с Proxy

# Получить всех пользователей
User.objects.all()  # 2 человека

# Получить только админов
Administrator.objects.all()  # 1 человек

# Получить только обычных пользователей
RegularUser.objects.all()  # 1 человек

# Отфильтровать по is_staff
User.objects.filter(is_staff=True)  # Админы

Преимущества Proxy

Одна таблица — нет дублирования данных
Нет migration — структура БД не меняется
Отдельный интерфейс — разные Admin views
Разная бизнес-логика — свои методы
Переиспользование — базовые поля есть

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

# ❌ Proxy НЕ подходит — нужны новые поля
class Student(Person):  # Нужны поля: student_id, gpa
    class Meta:
        proxy = True  # ПЛОХО!

# ✅ Используй наследование с новой таблицей
class Student(Person):
    student_id = models.CharField(max_length=20)
    gpa = models.FloatField()
    # Будет две таблицы: person и student

Типовые use cases

1. Роли пользователей

class User(models.Model):  # Одна таблица
    name = models.CharField()
    role = models.CharField()

class Moderator(User):
    proxy = True
    def ban_user(self, user): pass

class Contributor(User):
    proxy = True
    def create_post(self): pass

2. Состояния объектов

class Order(models.Model):
    status = models.CharField()

class PendingOrder(Order):
    proxy = True
    def approve(self): pass

class CompletedOrder(Order):
    proxy = True
    def archive(self): pass

3. Специализированные QuerySets

class PublishedPostManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(published=True)

class PublishedPost(Post):
    proxy = True
    objects = PublishedPostManager()

Итог

Proxy модель нужна для:

✅ Разных интерфейсов для одного объекта
✅ Разной бизнес-логики без дублирования данных
✅ Разных Admin views в Django
✅ Специализированных QuerySets

Не путать с наследованием моделей, которое создаёт новую таблицу.

Зачем нужна прокси модель в Django? | PrepBro