Экспорт в CSV из админки Django: пошаговая инструкция
Добавляем в админку Django кнопку, которая выгружает выбранные строки в CSV. На всё уйдёт минут двадцать, файл откроется в Excel и не превратится в кракозябры.

К концу этой инструкции у вас в списке объектов Django-админки появится действие «Экспортировать в CSV». Вы отмечаете галочками нужные строки, выбираете действие из выпадающего списка — браузер скачивает файл, который без плясок открывается в Excel и LibreOffice. Работы примерно на двадцать минут, если проект уже запущен. Всё делается штатными средствами Django, без сторонних пакетов.
Что нужно иметь до начала
Инструкция написана для Django 5.x, но тот же код работает начиная с Django 3.2 — API админ-действий и модуля csv за эти версии не менялся. Проверьте, что у вас есть:
- рабочий проект Django с запущенным сервером разработки (
python manage.py runserverотдаёт страницу); - хотя бы одна модель, зарегистрированная в админке, и суперпользователь для входа;
- доступ к файлу
admin.pyтого приложения, чью модель будете экспортировать.
Модуль csv входит в стандартную библиотеку Python — ставить ничего не нужно. Для примера возьмём приложение shop с моделью Order и полями id, customer, total, created_at. Подставьте свои имена.
Шаг 1. Пишем функцию-действие
Админ-действие в Django — это обычная функция, которая принимает три аргумента: сам объект админки, объект запроса и QuerySet с отмеченными строками. Откройте shop/admin.py и добавьте:
import csv
from django.http import HttpResponse
from django.contrib import admin
from .models import Order
def export_to_csv(modeladmin, request, queryset):
# Формируем HTTP-ответ, который браузер воспримет как файл для скачивания
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=orders.csv"
# BOM в начале файла — чтобы Excel правильно распознал кириллицу в UTF-8
response.write("\ufeff")
writer = csv.writer(response)
# Строка заголовков
writer.writerow(["ID", "Клиент", "Сумма", "Создан"])
for order in queryset:
writer.writerow([
order.id,
order.customer,
order.total,
order.created_at.strftime("%Y-%m-%d %H:%M"),
])
return response
# Текст, который увидит пользователь в выпадающем списке действий
export_to_csv.short_description = "Экспортировать в CSV"
Проверка: пока ничего не изменится — функцию мы объявили, но к админке не подключили. Ошибок при перезапуске сервера быть не должно.
Почему BOM и strftime
Строка "\ufeff" — это метка порядка байтов*. Без неё Excel в Windows часто открывает UTF-8 как Windows-1251 и вместо русских слов показывает набор символов. А created_at.strftime нужен, потому что объект datetime в CSV попадёт с часовым поясом и микросекундами — в таблице это выглядит грязно.
Шаг 2. Подключаем действие к админке
Теперь зарегистрируйте модель через класс ModelAdmin и передайте функцию в атрибут actions. Дописываем в тот же admin.py:
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ["id", "customer", "total", "created_at"]
actions = [export_to_csv]
Если модель уже была зарегистрирована через admin.site.register(Order), удалите ту строку — иначе Django выдаст AlreadyRegistered.
Проверка: перезапустите сервер, откройте список заказов в админке. Над таблицей появится выпадающий список «Action» (или «Действие»), а в нём — пункт «Экспортировать в CSV».
Шаг 3. Пробуем экспорт
- Отметьте галочками несколько строк в списке (или галочку в шапке — выделит всё на странице).
- Выберите в выпадающем списке «Экспортировать в CSV».
- Нажмите кнопку «Выполнить» (Go) справа от списка.
Проверка: браузер скачает файл orders.csv. Откройте его в Excel — в первой строке заголовки, дальше отмеченные заказы, кириллица читается.
Шаг 4. Убираем дублирование заголовков
Если полей много, руками перечислять их в двух местах (в заголовке и в строке данных) утомительно и легко ошибиться. Вынесите список полей в одну переменную:
def export_to_csv(modeladmin, request, queryset):
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=orders.csv"
response.write("\ufeff")
writer = csv.writer(response)
fields = ["id", "customer", "total", "created_at"]
writer.writerow(fields) # строка заголовков совпадает с именами полей
for obj in queryset:
writer.writerow([getattr(obj, field) for field in fields])
return response
export_to_csv.short_description = "Экспортировать в CSV"
Здесь getattr(obj, field) берёт значение поля по его имени. Дата в этом варианте выгрузится «как есть» — если нужен красивый формат, вернитесь к strftime для конкретного поля.
Экспорт из админки хорош для разовых выгрузок в десятки и сотни строк. Как только речь идёт о регулярных отчётах или десятках тысяч записей — это уже задача для отдельной вьюхи или management-команды, а не для кнопки в списке.
Если не получилось
| Что видите | Причина | Что делать |
|---|---|---|
AlreadyRegistered: The model Order is already registered | Модель зарегистрирована дважды | Уберите строку admin.site.register(Order), оставьте только декоратор @admin.register |
| В файле кракозябры вместо русских букв | Excel открыл UTF-8 без BOM | Убедитесь, что есть строка response.write("\ufeff") до записи данных |
| Пункта действия нет в списке | Действие не попало в actions или сервер не перезапущен | Проверьте атрибут actions = [export_to_csv] и перезапустите runserver |
AttributeError: 'Order' object has no attribute ... | В списке полей опечатка или такого поля нет | Сверьте имена в fields с полями модели в models.py |
| Скачивается страница целиком, а не файл | Функция не вернула HttpResponse | Проверьте, что в конце функции есть return response |
Что дальше
Кнопка работает — дальше можно сделать её удобнее. Вынесите функцию экспорта в метод класса OrderAdmin, если она нужна только этой модели. Если хотите экспортировать связанные объекты (например, товары внутри заказа), обращайтесь к ним через obj.items.all() внутри цикла. А чтобы не писать экспорт для каждой модели заново, посмотрите пакет django-import-export — он даёт выгрузку и загрузку в CSV, XLSX и JSON через отдельный класс-ресурс.
Магистр Excel и Google Sheets: ВПР, СУММЕСЛИМН, макросы VBA и скрипты Apps Script.
Спросить за 20 ₽Источники: Django admin actions — официальная документация, Python csv — модуль стандартной библиотеки
Частые вопросы
У меня Django 3.2, а не 5.x — код заработает?
Кириллица в Excel всё равно ломается, хотя BOM я добавил
Можно ли экспортировать сразу все записи, а не только отмеченные на странице?
Что будет с большим объёмом данных — десятки тысяч строк?
Как добавить в CSV поля из связанной модели?
Разделитель — запятая, а мой Excel ждёт точку с запятой. Что делать?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.