Введение
Работа с базой данных — неотъемлемая часть любой современной программы. Для Python существует множество ORM-библиотек, позволяющих удобно взаимодействовать с SQL-базами. Одной из самых простых и компактных является Peewee. Она идеально подходит для небольших проектов, скриптов и даже микросервисов.
Peewee – лёгкая ORM предлагает всё необходимое для описания моделей, выполнения запросов, работы с транзакциями и интеграции с веб-фреймворками — при этом её легко освоить и внедрить.
Принципы работы ORM и позиция Peewee
Что такое ORM
ORM (Object-Relational Mapping) — это технология, позволяющая работать с базами данных через объекты языка программирования. Вместо SQL-запросов вы создаёте и взаимодействуете с Python-классами, а ORM преобразует это в SQL.
ORM предоставляет абстракцию над SQL, что делает код более читаемым и менее подверженным ошибкам. Основные преимущества использования ORM:
- Автоматическое создание SQL-запросов
- Безопасность от SQL-инъекций
- Переносимость между разными СУБД
- Удобство работы с объектами Python
Когда выбирать Peewee
Peewee — отличный выбор в следующих случаях:
- Если нужен простой и лёгкий инструмент
- Если проект небольшой или экспериментальный
- Если важна скорость разработки без сложной конфигурации
- Для прототипирования и небольших веб-приложений
- Для настольных приложений с локальной базой данных
Установка и базовая настройка
Установка Peewee
Установить Peewee можно через pip:
pip install peewee
Для работы с различными СУБД потребуются дополнительные пакеты:
# Для PostgreSQL
pip install psycopg2-binary
# Для MySQL
pip install PyMySQL
# Для SQLite (входит в состав Python)
# дополнительных пакетов не требуется
Подключение к базе данных
Peewee поддерживает несколько типов баз данных:
from peewee import SqliteDatabase, PostgresqlDatabase, MySQLDatabase
# SQLite
db = SqliteDatabase('my_database.db')
# PostgreSQL
db = PostgresqlDatabase('mydb', user='username', password='password', host='localhost', port=5432)
# MySQL
db = MySQLDatabase('mydb', user='username', password='password', host='localhost', port=3306)
Создание моделей данных
Базовая модель
from peewee import Model, CharField, DateField, IntegerField, TextField
class BaseModel(Model):
class Meta:
database = db
class Author(BaseModel):
name = CharField(max_length=100)
birth_date = DateField()
bio = TextField(null=True)
class Book(BaseModel):
title = CharField(max_length=200)
isbn = CharField(max_length=13, unique=True)
pages = IntegerField()
publication_year = IntegerField()
author = ForeignKeyField(Author, backref='books')
Создание таблиц
db.connect()
db.create_tables([Author, Book])
Типы полей в Peewee
Основные типы полей
| Тип поля | Описание | Параметры |
|---|---|---|
| CharField | Строка с ограничением длины | max_length, unique, null |
| TextField | Текст без ограничения длины | null |
| IntegerField | Целое число | null, unique |
| FloatField | Число с плавающей точкой | null, unique |
| BooleanField | Булево значение | null, default |
| DateField | Дата | null, default |
| DateTimeField | Дата и время | null, default |
| TimeField | Время | null, default |
| DecimalField | Десятичное число | max_digits, decimal_places |
| BinaryField | Двоичные данные | null |
| UUIDField | UUID | null, default |
| AutoField | Автоинкрементное поле | primary_key=True |
Специальные поля
from peewee import ForeignKeyField, ManyToManyField
class Category(BaseModel):
name = CharField()
class Book(BaseModel):
title = CharField()
author = ForeignKeyField(Author, backref='books')
categories = ManyToManyField(Category, backref='books')
# Для many-to-many связей создается промежуточная таблица
BookCategory = Book.categories.get_through_model()
Операции с базой данных (CRUD)
Создание записей
# Создание одной записи
author = Author.create(name='Фёдор Достоевский', birth_date='1821-11-11')
# Создание через конструктор
author = Author(name='Лев Толстой', birth_date='1828-09-09')
author.save()
# Массовое создание
Author.insert_many([
{'name': 'Пушкин', 'birth_date': '1799-06-06'},
{'name': 'Лермонтов', 'birth_date': '1814-10-15'}
]).execute()
Чтение записей
# Получение всех записей
authors = Author.select()
for author in authors:
print(author.name)
# Получение одной записи
author = Author.get(Author.name == 'Фёдор Достоевский')
# Получение первой записи или None
author = Author.select().first()
# Получение записи по ID
author = Author.get_by_id(1)
Обновление записей
# Обновление одной записи
author = Author.get(Author.name == 'Фёдор Достоевский')
author.name = 'Ф. М. Достоевский'
author.save()
# Массовое обновление
Author.update(bio='Великий русский писатель').where(
Author.birth_date < '1850-01-01'
).execute()
Удаление записей
# Удаление одной записи
author = Author.get(Author.name == 'Лев Толстой')
author.delete_instance()
# Массовое удаление
Author.delete().where(Author.birth_date < '1800-01-01').execute()
Фильтрация, сортировка и агрегации
Фильтрация данных
# Простые условия
authors = Author.select().where(Author.name == 'Пушкин')
# Условия с операторами
authors = Author.select().where(Author.birth_date > '1800-01-01')
# Условие LIKE
authors = Author.select().where(Author.name.contains('Фёдор'))
# Условие IN
authors = Author.select().where(Author.name.in_(['Пушкин', 'Лермонтов']))
# Составные условия
authors = Author.select().where(
(Author.birth_date > '1800-01-01') &
(Author.name.contains('Лев'))
)
Сортировка
# По возрастанию
authors = Author.select().order_by(Author.name)
# По убыванию
authors = Author.select().order_by(Author.birth_date.desc())
# Множественная сортировка
authors = Author.select().order_by(Author.birth_date, Author.name)
Агрегации
from peewee import fn
# Подсчет записей
count = Author.select(fn.COUNT(Author.id)).scalar()
# Средний возраст
avg_age = Author.select(fn.AVG(Author.age)).scalar()
# Группировка
results = (Author
.select(Author.birth_date.year, fn.COUNT(Author.id))
.group_by(Author.birth_date.year))
Отношения между моделями
Один к одному (One-to-One)
class User(BaseModel):
username = CharField(unique=True)
email = CharField()
class UserProfile(BaseModel):
user = ForeignKeyField(User, backref='profile', unique=True)
bio = TextField()
avatar = CharField(null=True)
Один ко многим (One-to-Many)
class Author(BaseModel):
name = CharField()
class Book(BaseModel):
title = CharField()
author = ForeignKeyField(Author, backref='books')
Многие ко многим (Many-to-Many)
class Student(BaseModel):
name = CharField()
class Course(BaseModel):
name = CharField()
students = ManyToManyField(Student, backref='courses')
# Создание промежуточной таблицы
StudentCourse = Student.courses.get_through_model()
Продвинутые запросы
Объединения (JOIN)
# Простое объединение
query = (Book
.select(Book, Author)
.join(Author)
.where(Author.name == 'Пушкин'))
# Левое объединение
query = (Author
.select(Author, Book)
.join(Book, JOIN.LEFT_OUTER)
.where(Book.title.is_null(False)))
Подзапросы
# Подзапрос в условии WHERE
prolific_authors = Author.select().where(
Author.id.in_(
Book.select(Book.author).group_by(Book.author).having(fn.COUNT(Book.id) > 5)
)
)
# Подзапрос в SELECT
query = Author.select(
Author.name,
fn.COUNT(Book.id).alias('book_count')
).join(Book, JOIN.LEFT_OUTER).group_by(Author.id)
Сложные условия
# Использование Case
from peewee import Case
query = Author.select(
Author.name,
Case(None, [
(Author.birth_date < '1800-01-01', 'Старый'),
(Author.birth_date < '1850-01-01', 'Средний'),
], 'Новый').alias('period')
)
Транзакции в Peewee
Контекстный менеджер
try:
with db.atomic():
author = Author.create(name='Новый автор')
book = Book.create(title='Новая книга', author=author)
# Если произойдет ошибка, все изменения откатятся
except Exception as e:
print(f'Ошибка: {e}')
Ручное управление транзакциями
db.begin()
try:
Author.create(name='Автор 1')
Author.create(name='Автор 2')
db.commit()
except Exception:
db.rollback()
raise
Точки сохранения (Savepoints)
with db.atomic() as txn:
Author.create(name='Автор 1')
savepoint = txn.savepoint()
try:
Author.create(name='Автор 2')
# Что-то пошло не так
raise Exception('Ошибка!')
except Exception:
txn.rollback(savepoint)
Author.create(name='Автор 3')
Миграции в Peewee
Использование peewee-migrate
pip install peewee-migrate
from peewee_migrate import Router
router = Router(db)
# Создание миграции
router.create('add_email_to_author')
# Применение миграций
router.run()
Ручные миграции
def migrate_add_email_column():
migrator = SqliteMigrator(db)
email_field = CharField(default='')
migrate(
migrator.add_column('author', 'email', email_field),
)
Асинхронность и Peewee
Использование peewee-async
import asyncio
from peewee_async import Manager, PooledPostgresqlDatabase
database = PooledPostgresqlDatabase('test', max_connections=20)
objects = Manager(database)
async def handler():
await objects.create(Author, name='Async Author')
authors = await objects.execute(Author.select())
return authors
# Запуск асинхронного кода
loop = asyncio.get_event_loop()
authors = loop.run_until_complete(handler())
Интеграция с asyncio
import asyncio
import aiosqlite
from peewee import Model, CharField, SqliteDatabase
class AsyncAuthor(Model):
name = CharField()
class Meta:
database = SqliteDatabase(':memory:')
async def async_operations():
# Создание таблицы
AsyncAuthor.create_table()
# Асинхронные операции требуют специальных адаптеров
# или использования peewee-async
pass
Интеграция с веб-фреймворками
Flask
from flask import Flask, g
from peewee import SqliteDatabase
app = Flask(__name__)
db = SqliteDatabase('app.db')
@app.before_request
def before_request():
g.db = db
g.db.connect()
@app.after_request
def after_request(response):
g.db.close()
return response
# Использование с Flask-WTF
from flask_wtf import FlaskForm
from wtforms import StringField, validators
class AuthorForm(FlaskForm):
name = StringField('Name', [validators.Length(min=1, max=100)])
@app.route('/authors', methods=['GET', 'POST'])
def authors():
form = AuthorForm()
if form.validate_on_submit():
Author.create(name=form.name.data)
authors = Author.select()
return render_template('authors.html', authors=authors, form=form)
FastAPI
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import List
app = FastAPI()
class AuthorCreate(BaseModel):
name: str
birth_date: str
class AuthorResponse(BaseModel):
id: int
name: str
birth_date: str
class Config:
from_attributes = True
def get_db():
db.connect()
try:
yield db
finally:
db.close()
@app.post("/authors/", response_model=AuthorResponse)
async def create_author(author: AuthorCreate, db=Depends(get_db)):
author_obj = Author.create(**author.dict())
return AuthorResponse.from_orm(author_obj)
@app.get("/authors/", response_model=List[AuthorResponse])
async def get_authors(db=Depends(get_db)):
authors = Author.select()
return [AuthorResponse.from_orm(author) for author in authors]
Django (как альтернатива Django ORM)
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# models.py
from peewee import Model, CharField, SqliteDatabase
from django.conf import settings
db = SqliteDatabase(settings.DATABASES['default']['NAME'])
class Author(Model):
name = CharField()
class Meta:
database = db
# views.py
from django.shortcuts import render
from django.http import JsonResponse
def authors_list(request):
authors = list(Author.select().dicts())
return JsonResponse(authors, safe=False)
Работа с различными СУБД
SQLite
from peewee import SqliteDatabase
# Обычный файл
db = SqliteDatabase('myapp.db')
# В памяти (для тестов)
db = SqliteDatabase(':memory:')
# С дополнительными параметрами
db = SqliteDatabase('myapp.db', pragmas={
'journal_mode': 'wal',
'cache_size': -1024 * 64,
'foreign_keys': 1,
'ignore_check_constraints': 0,
'synchronous': 0
})
PostgreSQL
from peewee import PostgresqlDatabase
db = PostgresqlDatabase(
'mydb',
user='username',
password='password',
host='localhost',
port=5432,
autorollback=True,
autocommit=True,
options={
'sslmode': 'require',
'application_name': 'myapp'
}
)
MySQL
from peewee import MySQLDatabase
db = MySQLDatabase(
'mydb',
user='username',
password='password',
host='localhost',
port=3306,
charset='utf8mb4',
sql_mode='PIPES_AS_CONCAT',
use_unicode=True,
autocommit=True
)
Тестирование с Peewee
Настройка тестовой базы данных
import unittest
from peewee import SqliteDatabase
# Тестовая база в памяти
test_db = SqliteDatabase(':memory:')
class BaseTestCase(unittest.TestCase):
def setUp(self):
# Перенаправляем модели на тестовую базу
test_db.bind([Author, Book])
test_db.connect()
test_db.create_tables([Author, Book])
def tearDown(self):
test_db.drop_tables([Author, Book])
test_db.close()
class AuthorTestCase(BaseTestCase):
def test_create_author(self):
author = Author.create(name='Test Author', birth_date='1990-01-01')
self.assertEqual(author.name, 'Test Author')
self.assertEqual(Author.select().count(), 1)
def test_author_books(self):
author = Author.create(name='Test Author', birth_date='1990-01-01')
book = Book.create(title='Test Book', author=author)
self.assertEqual(author.books.count(), 1)
self.assertEqual(book.author, author)
Фикстуры для тестов
class TestFixtures:
@classmethod
def create_test_data(cls):
authors = [
Author.create(name='Автор 1', birth_date='1990-01-01'),
Author.create(name='Автор 2', birth_date='1985-05-15'),
]
books = [
Book.create(title='Книга 1', author=authors[0]),
Book.create(title='Книга 2', author=authors[1]),
]
return authors, books
Моки и тестирование запросов
from unittest.mock import patch, MagicMock
class DatabaseTestCase(BaseTestCase):
@patch('peewee.SqliteDatabase.execute_sql')
def test_query_execution(self, mock_execute):
mock_execute.return_value = MagicMock()
Author.select().execute()
# Проверяем, что запрос был выполнен
mock_execute.assert_called_once()
Расширенные возможности Peewee
Кастомные поля
from peewee import Field
import json
class JSONField(Field):
field_type = 'text'
def db_value(self, value):
return json.dumps(value)
def python_value(self, value):
if value is not None:
return json.loads(value)
class Config(BaseModel):
name = CharField()
settings = JSONField()
Валидация данных
from peewee import ValidationError
class Author(BaseModel):
name = CharField()
email = CharField()
def validate(self):
if '@' not in self.email:
raise ValidationError('Invalid email format')
if len(self.name) < 2:
raise ValidationError('Name too short')
def save(self, *args, **kwargs):
self.validate()
return super().save(*args, **kwargs)
Индексы и ограничения
class Book(BaseModel):
title = CharField()
isbn = CharField(unique=True)
author = ForeignKeyField(Author)
class Meta:
database = db
indexes = (
# Создание индекса по нескольким полям
(('title', 'author'), False),
# Уникальный индекс
(('isbn',), True),
)
# Ограничения на уровне таблицы
constraints = [
SQL('CHECK (length(title) > 0)')
]
Сигналы и хуки
from peewee import pre_save, post_save, pre_delete, post_delete
import datetime
@pre_save(sender=Author)
def author_pre_save(sender, instance, created):
if created:
instance.created_at = datetime.datetime.now()
@post_save(sender=Author)
def author_post_save(sender, instance, created):
if created:
print(f'Создан новый автор: {instance.name}')
@pre_delete(sender=Author)
def author_pre_delete(sender, instance):
print(f'Удаляется автор: {instance.name}')
Производительность и оптимизация
Оптимизация запросов
# Избегайте N+1 проблемы
# Плохо
authors = Author.select()
for author in authors:
print(author.books.count()) # Отдельный запрос для каждого автора
# Хорошо
authors = Author.select().annotate(book_count=fn.COUNT(Book.id)).join(
Book, JOIN.LEFT_OUTER
).group_by(Author.id)
for author in authors:
print(author.book_count)
Пагинация
def get_paginated_authors(page=1, per_page=10):
offset = (page - 1) * per_page
authors = Author.select().offset(offset).limit(per_page)
total = Author.select().count()
return {
'authors': list(authors),
'total': total,
'page': page,
'per_page': per_page,
'pages': (total + per_page - 1) // per_page
}
Пулы соединений
from peewee import PooledPostgresqlDatabase
from playhouse.pool import PooledMySQLDatabase
# PostgreSQL с пулом соединений
db = PooledPostgresqlDatabase(
'mydb',
max_connections=20,
stale_timeout=300,
user='username',
password='password'
)
# MySQL с пулом соединений
db = PooledMySQLDatabase(
'mydb',
max_connections=10,
stale_timeout=300,
user='username',
password='password'
)
Полная таблица методов и функций Peewee
| Категория | Метод/Функция | Описание | Пример использования |
|---|---|---|---|
| Подключение к БД | SqliteDatabase() |
Подключение к SQLite | db = SqliteDatabase('app.db') |
PostgresqlDatabase() |
Подключение к PostgreSQL | db = PostgresqlDatabase('mydb', user='user') |
|
MySQLDatabase() |
Подключение к MySQL | db = MySQLDatabase('mydb', user='user') |
|
db.connect() |
Открытие соединения | db.connect() |
|
db.close() |
Закрытие соединения | db.close() |
|
db.create_tables() |
Создание таблиц | db.create_tables([Model1, Model2]) |
|
db.drop_tables() |
Удаление таблиц | db.drop_tables([Model1, Model2]) |
|
| Создание записей | Model.create() |
Создание новой записи | Author.create(name='Пушкин') |
Model.save() |
Сохранение экземпляра | author.save() |
|
Model.insert() |
Вставка записи | Author.insert(name='Пушкин').execute() |
|
Model.insert_many() |
Массовая вставка | Author.insert_many([{'name': 'Пушкин'}]).execute() |
|
Model.bulk_create() |
Массовое создание | Author.bulk_create([author1, author2]) |
|
| Чтение записей | Model.select() |
Выборка записей | Author.select() |
Model.get() |
Получение одной записи | Author.get(Author.id == 1) |
|
Model.get_by_id() |
Получение по ID | Author.get_by_id(1) |
|
Model.get_or_none() |
Получение или None | Author.get_or_none(Author.id == 1) |
|
query.first() |
Первая запись | Author.select().first() |
|
query.scalar() |
Скалярное значение | Author.select(fn.COUNT()).scalar() |
|
query.count() |
Количество записей | Author.select().count() |
|
query.exists() |
Проверка существования | Author.select().exists() |
|
| Обновление записей | Model.update() |
Обновление записей | Author.update(name='Новое имя').execute() |
model.save() |
Сохранение изменений | author.save() |
|
Model.replace() |
Замена записи | Author.replace(id=1, name='Новое имя').execute() |
|
| Удаление записей | Model.delete() |
Удаление записей | Author.delete().where(Author.id == 1).execute() |
model.delete_instance() |
Удаление экземпляра | author.delete_instance() |
|
| Фильтрация | query.where() |
Условие WHERE | Author.select().where(Author.name == 'Пушкин') |
field == value |
Равенство | Author.name == 'Пушкин' |
|
field != value |
Неравенство | Author.name != 'Пушкин' |
|
field > value |
Больше | Author.age > 30 |
|
field < value |
Меньше | Author.age < 30 |
|
field >= value |
Больше или равно | Author.age >= 30 |
|
field <= value |
Меньше или равно | Author.age <= 30 |
|
field.in_(list) |
В списке | Author.name.in_(['Пушкин', 'Лермонтов']) |
|
field.not_in(list) |
Не в списке | Author.name.not_in(['Пушкин']) |
|
field.contains(value) |
Содержит | Author.name.contains('Пушк') |
|
field.startswith(value) |
Начинается с | Author.name.startswith('Пушк') |
|
field.endswith(value) |
Заканчивается на | Author.name.endswith('кин') |
|
field.is_null() |
Равно NULL | Author.bio.is_null() |
|
field.is_null(False) |
Не равно NULL | Author.bio.is_null(False) |
|
field.between(a, b) |
Между значениями | Author.age.between(20, 30) |
|
| Сортировка | query.order_by() |
Сортировка | Author.select().order_by(Author.name) |
field.asc() |
По возрастанию | Author.select().order_by(Author.name.asc()) |
|
field.desc() |
По убыванию | Author.select().order_by(Author.name.desc()) |
|
| Ограничения | query.limit() |
Лимит записей | Author.select().limit(10) |
query.offset() |
Смещение | Author.select().offset(10) |
|
query.paginate() |
Пагинация | Author.select().paginate(page=2, paginate_by=10) |
|
| Объединения | query.join() |
Объединение | Book.select().join(Author) |
query.switch() |
Переключение контекста | query.switch(Author) |
|
JOIN.LEFT_OUTER |
Левое объединение | Book.select().join(Author, JOIN.LEFT_OUTER) |
|
JOIN.RIGHT_OUTER |
Правое объединение | Book.select().join(Author, JOIN.RIGHT_OUTER) |
|
JOIN.FULL_OUTER |
Полное объединение | Book.select().join(Author, JOIN.FULL_OUTER) |
|
| Группировка | query.group_by() |
Группировка | Author.select().group_by(Author.country) |
query.having() |
Условие HAVING | query.having(fn.COUNT(Book.id) > 5) |
|
| Агрегации | fn.COUNT() |
Количество | Author.select(fn.COUNT(Author.id)) |
fn.SUM() |
Сумма | Book.select(fn.SUM(Book.price)) |
|
fn.AVG() |
Среднее | Book.select(fn.AVG(Book.price)) |
|
fn.MIN() |
Минимум | Book.select(fn.MIN(Book.price)) |
|
fn.MAX() |
Максимум | Book.select(fn.MAX(Book.price)) |
|
| Транзакции | db.atomic() |
Атомарная операция | with db.atomic(): ... |
db.transaction() |
Транзакция | with db.transaction(): ... |
|
db.begin() |
Начало транзакции | db.begin() |
|
db.commit() |
Коммит | db.commit() |
|
db.rollback() |
Откат | db.rollback() |
|
db.savepoint() |
Точка сохранения | sp = db.savepoint() |
|
| Связи | ForeignKeyField() |
Внешний ключ | author = ForeignKeyField(Author) |
ManyToManyField() |
Многие ко многим | categories = ManyToManyField(Category) |
|
backref |
Обратная связь | ForeignKeyField(Author, backref='books') |
|
| Валидация | model.validate() |
Валидация модели | author.validate() |
field.null |
Разрешить NULL | CharField(null=True) |
|
field.unique |
Уникальность | CharField(unique=True) |
|
field.default |
Значение по умолчанию | CharField(default='Unknown') |
|
| Метаданные | Model._meta |
Метаданные модели | Author._meta.database |
Model._meta.table_name |
Имя таблицы | Author._meta.table_name |
|
Model._meta.fields |
Поля модели | Author._meta.fields |
|
Model._meta.primary_key |
Первичный ключ | Author._meta.primary_key |
|
| Сырые запросы | Model.raw() |
Сырой SQL | Author.raw('SELECT * FROM author') |
db.execute_sql() |
Выполнение SQL | db.execute_sql('CREATE INDEX ...') |
|
| Сигналы | pre_save |
Перед сохранением | @pre_save(sender=Author) |
post_save |
После сохранения | @post_save(sender=Author) |
|
pre_delete |
Перед удалением | @pre_delete(sender=Author) |
|
post_delete |
После удаления | @post_delete(sender=Author) |
Сравнение с другими ORM
| ORM | Простота | Гибкость | Асинхронность | Производительность | Подходит для |
|---|---|---|---|---|---|
| Peewee | Очень высокая | Средняя | Частичная | Хорошая | Малые и средние проекты |
| SQLAlchemy | Средняя | Очень высокая | Да (2.0+) | Отличная | Средние и крупные проекты |
| Django ORM | Высокая | Высокая | Частично | Хорошая | Веб-приложения на Django |
| Tortoise ORM | Высокая | Высокая | Да | Отличная | Асинхронные приложения |
| Pony ORM | Очень высокая | Средняя | Нет | Хорошая | Быстрое прототипирование |
Лучшие практики
Структурирование моделей
# models.py
from peewee import Model, CharField, DateTimeField, ForeignKeyField
import datetime
class BaseModel(Model):
created_at = DateTimeField(default=datetime.datetime.now)
updated_at = DateTimeField(default=datetime.datetime.now)
class Meta:
database = db
def save(self, *args, **kwargs):
self.updated_at = datetime.datetime.now()
return super().save(*args, **kwargs)
class Author(BaseModel):
name = CharField(max_length=100)
email = CharField(unique=True)
def __str__(self):
return self.name
class Book(BaseModel):
title = CharField(max_length=200)
author = ForeignKeyField(Author, backref='books')
def __str__(self):
return f"{self.title} by {self.author.name}"
Использование менеджеров
class AuthorManager:
@staticmethod
def get_active_authors():
return Author.select().where(Author.is_active == True)
@staticmethod
def get_prolific_authors(min_books=5):
return (Author
.select()
.join(Book)
.group_by(Author)
.having(fn.COUNT(Book.id) >= min_books))
# Использование
active_authors = AuthorManager.get_active_authors()
prolific_authors = AuthorManager.get_prolific_authors(min_books=10)
Обработка исключений
from peewee import DoesNotExist, IntegrityError
try:
author = Author.get(Author.id == 999)
except DoesNotExist:
print("Автор не найден")
try:
Author.create(name="Дубликат", email="existing@email.com")
except IntegrityError:
print("Нарушение уникальности")
Сообщество и документация
Peewee имеет активное сообщество и отличную документацию:
- Официальная документация: http://docs.peewee-orm.com/
- GitHub: https://github.com/coleifer/peewee
- PyPI: https://pypi.org/project/peewee/
- Примеры кода: https://github.com/coleifer/peewee/tree/master/examples
Библиотека активно поддерживается и обновляется. Регулярно выходят новые версии с улучшениями производительности и новыми функциями.
Часто задаваемые вопросы (FAQ)
Что такое Peewee?
Peewee — это лёгкая и простая ORM-библиотека для Python, поддерживающая SQLite, PostgreSQL и MySQL. Она предоставляет интуитивный API для работы с базами данных через Python-объекты.
Подходит ли Peewee для крупных проектов?
Peewee в основном рекомендуется для небольших и средних проектов, хотя может использоваться и в более сложных решениях. Для крупных проектов с высокими требованиями к производительности лучше рассмотреть SQLAlchemy.
Есть ли у Peewee миграции?
Да, миграции доступны через сторонние инструменты, такие как peewee-migrate. Также можно создавать миграции вручную используя встроенные инструменты.
Поддерживает ли Peewee асинхронность?
Частично, через peewee-async и другие совместные решения. Для полноценной асинхронной работы рекомендуется использовать специализированные ORM типа Tortoise ORM.
Можно ли использовать Peewee с Flask и FastAPI?
Да, интеграция с обоими фреймворками возможна и хорошо документирована. Peewee легко интегрируется с любыми Python веб-фреймворками.
Как работает связь между моделями в Peewee?
Связи реализуются через ForeignKeyField для отношений один-ко-многим и ManyToManyField для отношений многие-ко-многим, аналогично другим ORM.
Можно ли использовать Peewee с другими СУБД кроме SQLite?
Да, Peewee поддерживает PostgreSQL, MySQL и SQLite. Для каждой СУБД существует соответствующий класс подключения.
Как обеспечить безопасность при работе с Peewee?
Peewee автоматически защищает от SQL-инъекций при использовании параметризованных запросов. Также следует валидировать входные данные и использовать аутентификацию.
Заключение
Peewee — это отличный выбор для разработчиков, которым нужна простая, но мощная ORM для Python. Она предоставляет все необходимые инструменты для работы с базами данных, оставаясь при этом легкой в изучении и использовании.
Основные преимущества Peewee:
- Простота освоения и использования
- Компактность и минимальные зависимости
- Хорошая документация и активное сообщество
- Поддержка основных СУБД
- Гибкость в настройке и расширении
Peewee идеально подходит для прототипирования, небольших приложений, скриптов автоматизации и проектов, где важна скорость разработки. Для крупных проектов с высокими требованиями к производительности стоит рассмотреть более мощные решения, такие как SQLAlchemy.
Настоящее и будущее развития ИИ: классической математики уже недостаточно
Эксперты предупредили о рисках фейковой благотворительности с помощью ИИ
В России разработали универсального ИИ-агента для роботов и индустриальных процессов