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

Как изменить отображение модели в админке?

1.3 Junior🔥 201 комментариев
#Django

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

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

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

Как изменить отображение модели в админке

В Django администраторская панель контролируется классом ModelAdmin, который определяет, как модели отображаются и редактируются в интерфейсе.

1. Базовое изменение отображения

# models.py
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.name

# admin.py
from django.contrib import admin
from .models import Product

# Вариант 1: Простая регистрация (минимальное отображение)
admin.site.register(Product)

# Вариант 2: Кастомизация через ModelAdmin
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    # Какие поля показывать в списке
    list_display = ('name', 'price', 'stock', 'created_at')
    
    # Какие поля доступны для фильтрации
    list_filter = ('created_at', 'price', 'stock')
    
    # Поиск по этим полям
    search_fields = ('name', 'description')
    
    # Какие поля редактируются в списке
    list_editable = ('price', 'stock')
    
    # Количество элементов на странице
    list_per_page = 50

2. Кастомизация деталей записи

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    # Порядок полей при редактировании
    fields = ('name', 'price', 'stock', 'description')
    
    # Группировка полей в категории (fieldsets)
    fieldsets = (
        ('Основная информация', {
            'fields': ('name', 'description')
        }),
        ('Цена и наличие', {
            'fields': ('price', 'stock')
        }),
        ('Служебная информация', {
            'fields': ('created_at',),
            'classes': ('collapse',)  # Свёрнутый блок
        })
    )
    
    # Поля, доступные только для чтения
    readonly_fields = ('created_at',)

3. Форматирование столбцов

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'formatted_price', 'stock_status', 'created_at')
    
    # Кастомный метод для отображения цены
    @admin.display(description='Цена ($)', ordering='price')
    def formatted_price(self, obj):
        return f"${obj.price:.2f}"
    
    # Кастомный метод для статуса наличия
    @admin.display(description='Статус', ordering='stock')
    def stock_status(self, obj):
        if obj.stock > 10:
            status = "✓ В наличии"
            color = "green"
        elif obj.stock > 0:
            status = "⚠ Заканчивается"
            color = "orange"
        else:
            status = "✗ Нет в наличии"
            color = "red"
        
        return f'<span style="color: {color};">{status}</span>'
    
    # Разрешить HTML в таблице
    formatted_price.allow_tags = True

4. Встроенное редактирование связанных моделей

from django.contrib import admin
from .models import Product, Review

# Встроенное редактирование отзывов внутри Product
class ReviewInline(admin.TabularInline):
    model = Review
    extra = 1  # Дополнительная пустая форма
    fields = ('author', 'rating', 'comment')
    readonly_fields = ('created_at',)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'price')
    inlines = [ReviewInline]  # Встроить Review в Product

5. Кастомные действия (actions)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'price', 'stock')
    actions = ['mark_out_of_stock', 'apply_discount']
    
    @admin.action(description="Отметить как нет в наличии")
    def mark_out_of_stock(self, request, queryset):
        updated = queryset.update(stock=0)
        self.message_user(request, f"{updated} товаров отмечено как нет в наличии")
    
    @admin.action(description="Применить 20% скидку")
    def apply_discount(self, request, queryset):
        for product in queryset:
            product.price *= 0.8
            product.save()
        self.message_user(request, f"Скидка применена {queryset.count()} товарам")

6. Сортировка и фильтры

from django.contrib import admin
from django.utils.html import format_html
from datetime import datetime, timedelta

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'price', 'stock')
    list_filter = (
        'created_at',
        ('price', admin.NumericRangeFilter),  # Фильтр по диапазону
        ('stock', admin.BooleanFieldListFilter),  # Есть / нет
    )
    
    # Сортировка по умолчанию
    ordering = ('-created_at',)  # Новые первыми
    
    # Дополнительная фильтрация
    def get_list_filter(self, request):
        list_filter = list(self.list_filter)
        if request.user.is_superuser:
            list_filter.append('is_archived')
        return list_filter

7. Поиск с улучшениями

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    # Поиск по нескольким полям
    search_fields = ('name', 'description', 'sku')
    
    # Кастомный поиск
    def get_search_results(self, request, queryset, search_term):
        queryset, use_distinct = super().get_search_results(
            request, queryset, search_term
        )
        
        # Дополнительная фильтрация
        try:
            price = float(search_term)
            queryset |= self.model.objects.filter(price=price)
        except ValueError:
            pass
        
        return queryset, use_distinct

8. Кастомные форма и валидация

from django import forms
from django.contrib import admin

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = '__all__'
    
    def clean(self):
        cleaned_data = super().clean()
        
        # Валидация цены
        if cleaned_data.get('price') and cleaned_data['price'] < 0:
            raise forms.ValidationError("Цена не может быть отрицательной")
        
        # Валидация наличия
        if cleaned_data.get('stock') and cleaned_data['stock'] < 0:
            raise forms.ValidationError("Наличие не может быть отрицательным")
        
        return cleaned_data

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    form = ProductForm

9. Изменение цвета и стилей

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'colored_price', 'stock')
    
    @admin.display(description='Цена')
    def colored_price(self, obj):
        if obj.price > 100:
            color = 'red'
        elif obj.price > 50:
            color = 'orange'
        else:
            color = 'green'
        
        return format_html(
            '<span style="color: {}; font-weight: bold;">${:.2f}</span>',
            color, obj.price
        )

10. Разрешения и видимость

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request):
        # Некоторые поля только для суперпользователя
        if request.user.is_superuser:
            return self.readonly_fields
        return list(self.readonly_fields) + ['created_at', 'price']
    
    def get_list_display(self, request):
        # Разные столбцы для разных пользователей
        if request.user.is_staff and not request.user.is_superuser:
            return ('name', 'stock')
        return self.list_display
    
    def get_queryset(self, request):
        # Фильтрация данных по пермиссиям
        qs = super().get_queryset(request)
        if not request.user.is_superuser:
            qs = qs.filter(is_archived=False)
        return qs

11. Полный пример

from django.contrib import admin
from django.utils.html import format_html
from .models import Product

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    # Список
    list_display = ('name', 'formatted_price', 'stock_status', 'created_date')
    list_filter = ('created_at', 'price')
    search_fields = ('name', 'description')
    list_per_page = 25
    ordering = ('-created_at',)
    
    # Редактирование
    fieldsets = (
        ('Основное', {
            'fields': ('name', 'description')
        }),
        ('Цена и наличие', {
            'fields': ('price', 'stock')
        }),
        ('Служебное', {
            'fields': ('created_at',),
            'classes': ('collapse',)
        })
    )
    readonly_fields = ('created_at',)
    
    # Действия
    actions = ['mark_out_of_stock']
    
    @admin.display(description='Цена')
    def formatted_price(self, obj):
        return f"${obj.price:.2f}"
    
    @admin.display(description='Статус')
    def stock_status(self, obj):
        status = "Да" if obj.stock > 0 else "Нет"
        color = "green" if obj.stock > 0 else "red"
        return format_html(
            '<span style="color: {};">{}</span>',
            color, status
        )
    
    @admin.display(description='Дата', ordering='created_at')
    def created_date(self, obj):
        return obj.created_at.strftime('%d.%m.%Y')
    
    @admin.action(description="Отметить нет в наличии")
    def mark_out_of_stock(self, request, queryset):
        updated = queryset.update(stock=0)
        self.message_user(request, f"Обновлено {updated} товаров")

Заключение

Кастомизация админки включает:

  • list_display — столбцы в списке
  • list_filter — фильтры
  • search_fields — поиск
  • fieldsets — группировка полей
  • readonly_fields — поля только для чтения
  • actions — кастомные действия
  • inlines — встроенное редактирование связанных моделей
  • форматирование — кастомные методы и HTML

Правильно настроенная админка значительно упрощает работу с данными!

Как изменить отображение модели в админке? | PrepBro