Установка библиотек и подключение базы данных PostgreSQL к приложению
Введение
В рамках этого учебного курса мы будем разрабатывать веб-приложение с использованием Python, FastAPI и базы данных PostgreSQL.
Для взаимодействия между приложением и базой данных мы будем использовать библиотеку psycopg
, официальную реализацию PostgreSQL-драйвера для Python.
psycopg
— это высокопроизводительный адаптер PostgreSQL для Python. В версии 3 он стал более гибким, поддерживает как синхронный, так и асинхронный режим работы, удобную работу с типами PostgreSQL, пул подключений и многое другое.
Мы будем использовать синхронный интерфейс, чтобы сфокусироваться на базовых принципах работы с базой данных — без усложнения кода корутинами.
Преимущества psycopg
:
- высокая производительность;
- простота подключения;
- поддержка параметризованных запросов (безопасность от SQL-инъекций);
- поддержка пулов соединений;
- хорошая совместимость с PostgreSQL-типами (
JSON
,UUID
,ARRAY
,TIMESTAMP
и т.д.); - активная поддержка и документация.
Благодаря этому мы сможем использовать всю мощь PostgreSQL прямо из кода на Python — просто, эффективно и без необходимости использовать ORM.
Основы API библиотеки psycopg
Библиотека psycopg
предоставляет несколько ключевых интерфейсов для работы с PostgreSQL в соответствии со стандартом Python DB API 2.0.
В этом разделе мы рассмотрим три основных компонента:
- Прямое подключение через
connect()
; - Пул подключений через
ConnectionPool
; - Работа с результатами запросов через
cursor
.
1. Подключение напрямую через connect()
Функция psycopg.connect()
позволяет установить одно явное соединение с базой данных PostgreSQL.
Это базовый интерфейс, полезный для простых операций и ручного управления транзакциями.
Пример:
import psycopg
conn = psycopg.connect(
host="localhost",
port=5432,
user="postgres",
password="secret",
dbname="mydb"
)
with conn.cursor() as cur:
cur.execute("SELECT NOW()")
result = cur.fetchone()
print(result)
conn.close()
Особенности:
- Соединение создаётся вручную и закрывается вручную (
conn.close()
). - По умолчанию
psycopg
использует неавтоматические транзакции: нужно вызыватьconn.commit()
илиconn.rollback()
.
Параметры подключения:
psycopg.connect(
host="localhost",
port=5432,
user="postgres",
password="secret",
dbname="mydb",
connect_timeout=10,
application_name="myapp"
)
Также можно использовать DSN-строку:
psycopg.connect("postgresql://postgres:secret@localhost:5432/mydb")
2. Пул подключений (рекомендуемый способ)
Объект ConnectionPool
из модуля psycopg_pool
управляет множеством соединений с базой данных. Это более производительный и устойчивый способ подключения в реальных приложениях.
Пример:
from psycopg_pool import ConnectionPool
pool = ConnectionPool(conninfo="postgresql://postgres:secret@localhost:5432/mydb")
with pool.connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT * FROM users WHERE id = %s", (1,))
row = cur.fetchone()
print(row)
Преимущества:
- Повторное использование уже открытых соединений;
- Подходит для приложений с высокой нагрузкой;
Конфигурация пула:
pool = ConnectionPool(
conninfo="...",
min_size=1, # минимальное количество соединений
max_size=10, # максимальное количество соединений
max_idle=30, # максимальное время простоя соединения (секунды)
timeout=5 # таймаут ожидания соединения из пула (секунды)
)
3. Результаты выполнения SQL-запросов
После выполнения cursor.execute(...)
результат запроса доступен через курсор. Типичный объект результата включает:
cursor.fetchone()
— получить одну строку;cursor.fetchall()
— получить все строки;cursor.rowcount
— количество строк, возвращённых или затронутых;cursor.description
— метаинформация о колонках (имена, типы и т.д.).
Пример:
with conn.cursor() as cur:
cur.execute("SELECT 1 AS one, 2 AS two;")
print(cur.description[0].name) # 'one'
print(cur.description[1].name) # 'two'
print(cur.fetchall()) # [(1, 2)]
Если нужно получать строки в виде словарей (dict
), можно задать row_factory
:
with conn.cursor(row_factory=psycopg.rows.dict_row) as cur:
cur.execute("SELECT id, name FROM users;")
for row in cur.fetchall():
print(row["id"], row["name"])
Структура проекта
Создайте каталог gophertalk-backend-fastapi
. В нем создайте подкаталог src
, файлы .env
, requirements.txt
, README.md
. В каталоге src
создайте каталоги, указанные ниже, а также пустой файл app.py
. Также создайте каталог __tests__
и подкаталоги в нем.
gophertalk-backend-fastapi/
├── src/
│ ├── controllers/ # Обработка HTTP-запросов
│ ├── services/ # Бизнес-логика
│ ├── repositories/ # Работа с БД (SQL-запросы)
│ ├── routes/ # Определение маршрутов
│ ├── dependencies/ # Общие зависимости FastAPI
│ ├── packages/ # скачанные пакеты с зависимостями
│ ├── config/ # Конфигурация проекта
│ ├── utils/ # Вспомогательные функции
│ ├── dto/ # Data transfer objects и pydantic модели
│ └── app.py # Инициализация приложения
├── __tests__ # unit тесты
│ ├── controllers/
│ ├── services/
│ └── repositories/
├── .env # Переменные окружения
├── requirements.txt
└── README.md
Инициализация проекта и установка зависимостей
В корне проекта в папке gophertalk-backend-fastapi
создайте виртуальное окружение и активируйте его:
python3 -m venv .venv
source .venv/bin/activate
Поместите в файл requirements.txt
содержимое:
fastapi
psycopg
psycopg-binary
psycopg_pool
python-dotenv
pydantic
python-jose[cryptography]
bcrypt
httpx
pytest
uvicorn[standard]
pydantic
regex
Выполните команду
pip install -r requirements.txt
Установка переменных окружения
Использование переменных окружения в проекте позволяет отделить конфиденциальные и изменяемые настройки (например, параметры подключения к базе данных) от основного кода приложения. Это важно по нескольким причинам.
Во-первых, безопасность: данные вроде логина, пароля, адреса сервера и имени базы данных не должны попадать в систему контроля версий (например, Git), чтобы избежать утечек при публикации кода. Переменные окружения можно хранить в .env файле, который добавляется в .gitignore
, или задавать напрямую в среде запуска (например, на сервере или в CI/CD).
Во-вторых, гибкость и удобство настройки: приложение можно разворачивать в разных средах — локально, на тестовом сервере, в продакшене — без изменения исходного кода. Достаточно задать переменные окружения для каждой среды.
В-третьих, читаемость и масштабируемость: конфигурационные значения собраны в одном месте, их проще менять и документировать. Это особенно важно в командной разработке и при работе с множеством сервисов и баз данных.
Для удобства разработки мы используем пакет python-dotenv
, который умеет считывать переменные из файла .env
. Пример заполнения этого файла представлен ниже.
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=postgres
ACCESS_TOKEN_EXPIRES=3600
REFRESH_TOKEN_EXPIRES=86400
ACCESS_TOKEN_SECRET=super_secret_access_token_key
REFRESH_TOKEN_SECRET=super_secret_refresh_token_key
Значения переменных установите сами. Вам нужен сервер PostgreSQL, база данных в нем и учетная запись с правами в этой БД.
Значения переменных ACCESS_TOKEN_EXPIRES
, REFRESH_TOKEN_EXPIRES
, ACCESS_TOKEN_SECRET
и REFRESH_TOKEN_SECRET
не изменяйте, они понадобятся в дальнейшем.
Настройка подключения к PostgreSQL
Создайте файл db.py
в каталоге src/config
. Поместите в него следующий код:
import os
from psycopg_pool import ConnectionPool
pool = ConnectionPool(
conninfo=f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}"
f"@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"
)
Разберем, что происходит в этом коде.
Импорт библиотек
pythonimport os from psycopg_pool import ConnectionPool
os
— стандартный модуль для работы с переменными окружения (os.getenv()
).ConnectionPool
— создаёт и управляет пулом соединений к PostgreSQL.
Создание пула подключений
pythonpool = ConnectionPool(conninfo=...)
- Параметры подключения собираются в строку
conninfo
, которая поддерживает стандартный синтаксис PostgreSQL:postgresql://user:password@host:port/dbname
. - Пул будет автоматически переиспользовать соединения и управлять ими.
- Параметры подключения собираются в строку
Создание главного файла приложения, запуск приложения и проверка подключения к БД
Создайте файл app.py
в каталоге src/
. Поместите в него следующее содержимое:
import os
from dotenv import load_dotenv
from fastapi import FastAPI, Response, status
load_dotenv()
from config.db import pool
app = FastAPI()
port = int(os.getenv("PORT", 3000))
@app.get("/api/health-check")
def health_check():
try:
with pool.connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1")
cur.fetchone()
return Response(content="OK", status_code=status.HTTP_200_OK)
except Exception:
return Response(content="DB connection failed", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=True)
Этот код — минимальный сервер на FastAPI, подключённый к базе данных PostgreSQL через psycopg
. Разберём его подробнее.
1. Импорт библиотек
import os
from dotenv import load_dotenv
from fastapi import FastAPI, Response, status
from src.config.db import pool
os
— стандартный модуль Python для работы с переменными окружения (os.getenv()
).load_dotenv()
— загружает переменные из.env
файла вos.environ
.- FastAPI — фреймворк для создания веб-сервера и REST API.
pool
— пул подключений к PostgreSQL, созданный вsrc/config/db.py
.
2. Загрузка переменных окружения
load_dotenv()
- Загружает переменные из файла
.env
в глобальное окружение. - После этого можно безопасно использовать
os.getenv("DB_USER")
и другие переменные в коде.
3. Создание приложения и определение порта
app = FastAPI()
port = int(os.getenv("PORT", 3000))
app
— это экземпляр FastAPI-приложения. Через него определяются маршруты,middlewares
и обработчики ошибок.port
— порт, на котором будет запущено приложение (берётся из.env
или устанавливается значение по умолчанию —3000
).
4. Маршрут /api/health-check
@app.get("/api/health-check")
def health_check():
try:
with pool.connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1")
cur.fetchone()
return Response(content="OK", status_code=status.HTTP_200_OK)
except Exception:
return Response(content="DB connection failed", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
- Это технический
GET
-маршрут для проверки работоспособности приложения и соединения с PostgreSQL. - Выполняется простой запрос
SELECT 1
внутри пула подключений. - Если соединение успешно — возвращается
200 OK
, иначе500 DB connection failed
.
HTTP коды
HTTP-статус-коды делятся на 5 категорий, каждая из которых имеет своё назначение. Вот некоторые из них:
🔵 1xx — Информационные (Informational)
Код | Назначение |
---|---|
100 | Продолжайте (Continue) — сервер получил заголовки и ждёт тело запроса |
101 | Переключение протоколов (Switching Protocols) — например, WebSocket |
🟢 2xx — Успешные (Success)
Код | Назначение |
---|---|
200 | OK — успешный запрос |
201 | Created — успешно создан ресурс (чаще при POST) |
204 | No Content — запрос успешен, но тело ответа отсутствует (например, DELETE) |
🟡 3xx — Перенаправление (Redirection)
Код | Назначение |
---|---|
301 | Moved Permanently — постоянное перенаправление |
302 | Found — временное перенаправление |
304 | Not Modified — использовать закешированную версию ресурса |
🔴 4xx — Ошибки клиента (Client Errors)
Код | Назначение |
---|---|
400 | Bad Request — некорректный запрос |
401 | Unauthorized — требуется авторизация |
403 | Forbidden — доступ запрещён, даже при наличии авторизации |
404 | Not Found — запрашиваемый ресурс не найден |
409 | Conflict — конфликт запроса (например, при создании дубликата) |
422 | Unprocessable Entity — ошибка обработки данных (например, ошибка валидации формы) |
🔴 5xx — Ошибки сервера (Server Errors)
Код | Назначение |
---|---|
500 | Internal Server Error — внутренняя ошибка сервера |
502 | Bad Gateway — неверный ответ от стороннего сервера |
503 | Service Unavailable — сервер временно недоступен (например, перегружен) |
504 | Gateway Timeout — истекло время ожидания ответа от другого сервиса |
HTTP методы
HTTP-методы определяют тип действия, которое клиент (например, браузер или frontend-приложение) хочет выполнить на сервере по заданному URL. Они являются основой для построения REST API и позволяют реализовывать операции чтения, создания, обновления и удаления ресурсов.
Каждый метод имеет своё назначение и семантику, и его правильное использование помогает сделать API логичным, безопасным и удобным для использования.
Метод | Назначение | Идёмпотентность | Безопасность | Используется в REST для |
---|---|---|---|---|
GET | Получение данных с сервера | ✅ Да | ✅ Да | Чтение |
POST | Отправка новых данных на сервер (создание ресурса) | ❌ Нет | ❌ Нет | Создание |
PUT | Полное обновление ресурса (замена) | ✅ Да | ❌ Нет | Обновление |
PATCH | Частичное обновление ресурса | ❌ Нет | ❌ Нет | Частичное обновление |
DELETE | Удаление ресурса | ✅ Да | ❌ Нет | Удаление |
HEAD | Как GET , но без тела ответа (используется для проверки заголовков, кеша и т.д.) | ✅ Да | ✅ Да | Проверка доступности |
OPTIONS | Возвращает допустимые методы для указанного ресурса (применяется для CORS) | ✅ Да | ✅ Да | Обнаружение возможностей |
Если метод идемпотентен, это значит, что повторный вызов этого метода не изменит результат. Например:
GET /users
вернёт один и тот же список при каждом вызове;DELETE /user/5
удалит пользователя, и повторный вызов уже ничего не изменит (если пользователь был удалён в первый раз);POST /users
не идемпотентен — при каждом вызове может создаваться новый пользователь.
Безопасный HTTP-метод — это метод, который не изменяет состояние сервера. Он используется только для получения информации, и его выполнение не должно иметь побочных эффектов (например, создавать, изменять или удалять данные). Например:
GET
— безопасен, потому что просто читает данные;POST
— не безопасен, потому что может создавать ресурсы или выполнять действия.
5. Запуск сервера
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=PORT, reload=True)
- Это точка входа для запуска приложения.
- Используется сервер
uvicorn
, совместимый сASGI
, который запускает FastAPI-приложение. - Аргумент
"app:app"
означает: импортировать объектapp
из файлаapp.py
. - Опция
reload=True
автоматически перезапускает сервер при изменении кода (удобно при разработке).
Чтобы запустить наше приложение, выполните команду
python3 src/app.py
Если все сделано правильно, то в консоли будет вывод:
INFO: Uvicorn running on http://0.0.0.0:3000 (Press CTRL+C to quit)
INFO: Started reloader process [90112] using WatchFiles
INFO: Started server process [90118]
INFO: Waiting for application startup.
INFO: Application startup complete.
Чтобы проверить, что подключение к БД выполнено успешно, необходимо сделать GET
запрос по адресу http://localhost:3000/api/health-check
. Сделать это можно несколькими способами.
С помощью утилиты
curl
В другой консоли (так как в первой у нас запущено приложение) выполните команду:
bashcurl http://localhost:3000/api/health-check
В случае успеха вы увидите ответ
OK
. Иначе в консоли с приложением будет ошибка.С помощью браузера.
Откройте любой браузер и перейдите по адресу
http://localhost:3000/api/health-check
. При таком действии браузер отправляетGET
запрос. Если все нормально, то вы также увидите текстOk
.С помощью программного обеспечения Postman, об этом далее.
Итог
Мы создали минимальный, но уже работоспособный каркас веб-приложения на основе FastAPI.
Вы узнали, как:
- подключать необходимые зависимости (
fastapi
,psycopg
,python-dotenv
и др.); - использовать
.env
файл для хранения конфигурации; - подключаться к PostgreSQL через пул соединений (
psycopg_pool
); - реализовать базовую маршрутизацию (
/api/health-check
) для проверки доступности БД;
Этот фундаментальный каркас послужит основой для дальнейшей разработки полноценного REST API.