Список записей из базы на странице в Django: шаг за шагом
Пройдём путь от пустого проекта до страницы, которая показывает записи из базы: модель, миграция, представление, шаблон и маршрут. На всё уйдёт около получаса.

К концу этого гайда у вас будет страница по адресу /posts/, которая читает записи из базы SQLite и выводит их списком в браузере. Мы создадим модель, применим миграцию, добавим пару записей через админку, напишем представление и шаблон. На всё уходит примерно 30–40 минут, если Python уже установлен. Пишу под Django 5.x и Python 3.10+ — на 4.2 всё работает так же, отличий в этих шагах нет.
Что нужно иметь до начала
Проверьте это заранее, чтобы не застрять на середине:
- Python 3.10 или новее. Проверка:
python --versionв терминале. - Установленный Django. Если его нет:
pip install django. Проверка:django-admin --version— должна вывести номер вроде5.1.4. - Созданный проект и приложение. Если их ещё нет, команды ниже создадут их. Работаем в базе SQLite — она идёт с Django из коробки, отдельно ничего ставить не надо.
- Любой редактор кода. VS Code, PyCharm, что угодно.
Дальше я предполагаю, что вы запускаете команды из папки, где лежит файл manage.py.
Шаг 1. Создаём проект и приложение
Если проекта ещё нет, выполните по очереди:
django-admin startproject mysite
cd mysite
python manage.py startapp blog
Первая команда создаёт папку mysite с настройками. Вторая — приложение blog, где будет жить наша модель. Проверка: рядом с manage.py появилась папка blog с файлами models.py, views.py и другими.
Подключаем приложение
Django не видит приложение, пока оно не прописано в настройках. Откройте mysite/settings.py, найдите список INSTALLED_APPS и добавьте туда строку 'blog',:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # наше приложение
]
Проверка: запустите python manage.py check — должно вывести System check identified no issues (0 silenced).
Шаг 2. Описываем модель
Модель — это класс Python, который Django превращает в таблицу базы. Один экземпляр модели = одна строка в таблице. Откройте blog/models.py и замените содержимое на это:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200) # заголовок, строка
body = models.TextField() # текст записи
created_at = models.DateTimeField(auto_now_add=True) # дата создания
def __str__(self):
# так запись подписывается в админке и в консоли
return self.title
Метод __str__ не обязателен для работы, но без него в админке записи будут называться Post object (1) вместо заголовка. Проверка: python manage.py check снова проходит без ошибок.
Шаг 3. Создаём и применяем миграцию
Миграция — файл, который описывает изменения структуры базы. Django генерирует его сам, а потом «накатывает» на базу.
python manage.py makemigrations
python manage.py migrate
Первая команда должна вывести что-то вроде Migrations for 'blog': blog/migrations/0001_initial.py - Create model Post. Вторая — список применённых миграций, заканчивающийся на Applying blog.0001_initial... OK. После этого в файле базы db.sqlite3 появилась таблица blog_post.
Порядок здесь строгий: сначалаmakemigrations(создать файл миграции), потомmigrate(применить его к базе). Если поменяли модель, но забыли пересоздать миграцию, база останется старой — и вы получите ошибку об отсутствующем столбце.
Шаг 4. Добавляем записи через админку
Чтобы было что выводить, наполним таблицу. Проще всего — через встроенную админку. Сначала зарегистрируем модель. Откройте blog/admin.py:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Теперь создайте пользователя-администратора и запустите сервер:
python manage.py createsuperuser
python manage.py runserver
Команда createsuperuser спросит имя, email и пароль. Пароль вводится вслепую — символы не отображаются, это нормально. Откройте http://127.0.0.1:8000/admin/, войдите, найдите раздел Posts и добавьте две-три записи. Проверка: в списке админки видны заголовки, которые вы ввели.
Шаг 5. Пишем представление
Представление (view) — функция, которая забирает данные из базы и отдаёт браузеру. Откройте blog/views.py:
from django.shortcuts import render
from .models import Post
def post_list(request):
# берём все записи, новые сверху
posts = Post.objects.all().order_by('-created_at')
# передаём их в шаблон под именем posts
return render(request, 'blog/post_list.html', {'posts': posts})
Post.objects.all() возвращает все строки таблицы. Метод order_by('-created_at') сортирует по дате: минус означает «по убыванию», то есть свежие записи окажутся первыми.
Шаг 6. Создаём шаблон
Django по умолчанию ищет шаблоны в папке templates внутри каждого приложения. Создайте папку и файл: blog/templates/blog/post_list.html. Вложенная папка blog внутри templates — не опечатка, она нужна, чтобы шаблоны разных приложений не перепутались.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Записи</title>
</head>
<body>
<h1>Записи</h1>
<ul>
{% for post in posts %}
<li>
<strong>{{ post.title }}</strong>
<p>{{ post.body }}</p>
</li>
{% empty %}
<li>Записей пока нет.</li>
{% endfor %}
</ul>
</body>
</html>
Блок {% for %}...{% endfor %} перебирает записи, а {% empty %} сработает, если список пуст — тогда вместо пустоты вы увидите понятное сообщение.
Шаг 7. Прописываем маршрут
Осталось связать URL с представлением. Создайте файл blog/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('posts/', views.post_list, name='post_list'),
]
Теперь подключите этот файл к главным маршрутам проекта. Откройте mysite/urls.py и приведите его к такому виду:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), # подключаем маршруты приложения
]
Функция include передаёт все маршруты из blog/urls.py. Проверка: если сервер остановлен, запустите python manage.py runserver и откройте http://127.0.0.1:8000/posts/. В браузере появится заголовок «Записи» и ваш список.
Если не получилось
Пять ошибок, которые встречаются чаще всего:
TemplateDoesNotExist: blog/post_list.html— шаблон лежит не там. Путь должен быть ровноblog/templates/blog/post_list.html, с двойной вложенностьюblog. Проверьте, что имя файла вrender()совпадает.no such table: blog_post— забыли применить миграцию. Выполнитеpython manage.py makemigrations, затемpython manage.py migrate.ModuleNotFoundError: No module named 'blog'или раздел не виден в админке — приложение не добавлено вINSTALLED_APPSвsettings.py.Page not found (404)на/posts/— маршрут не подключён. Проверьте, что вmysite/urls.pyесть строка сinclude('blog.urls'), а вblog/urls.py—path('posts/', ...).- Страница открывается, но список пустой — в базе нет записей. Добавьте их через админку на
/admin/или проверьте, что записи действительно сохранились.
Что делать дальше
Список работает — теперь стоит освоить соседние шаги. Первый — страница отдельной записи: добавьте маршрут вида path('posts/<int:pk>/', ...) и представление, которое берёт одну запись через get_object_or_404. Второй — пагинация: когда записей станет много, класс Paginator из django.core.paginator разобьёт их по страницам. Третий — замена функции на class-based view ListView, который делает то же самое короче. Начните с документации по работе с запросами к базе — ссылка ниже.
Транскрибатор встреч: Запись созвона или диктофона — в текст с разделением по спикерам, протоколом и списком задач.
Спросить за 25 ₽Источники: Django: Writing your first Django app, part 3 (views and templates), Django: Making queries (QuerySet API)
Частые вопросы
У меня Django 4.2, а не 5.x — всё сработает?
Страница показывает 'no such table: blog_post' — что делать?
Обязательно ли выводить записи через админку — можно ли добавить их из кода?
Почему шаблон лежит в blog/templates/blog/, а не просто в blog/templates/?
Можно ли вместо функции сразу использовать ListView?
Записей стало много, страница грузится долго — что предпринять?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.