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

Приведи пример использования ORM в Python

2.0 Middle🔥 131 комментариев
#Django#Базы данных (SQL)

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

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

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

Краткий ответ

ORM (Object-Relational Mapping) — инструмент для работы с БД через объекты вместо SQL. Основные ORM для Python: SQLAlchemy (универсальный), Django ORM (встроенный в Django), Tortoise ORM (асинхронный). Покажу примеры всех трёх с одинаковой моделью.

Что такое ORM?

ORM преобразует таблицы БД в классы Python:

Таблица users в БД:
+----+-------+--------------------+-----+
| id | name  | email              | age |
+----+-------+--------------------+-----+
| 1  | John  | john@example.com   | 30  |
| 2  | Jane  | jane@example.com   | 28  |
+----+-------+--------------------+-----+

Объект User в Python:
class User:
    id = 1
    name = 'John'
    email = 'john@example.com'
    age = 30

Пример 1: SQLAlchemy (самый популярный)

from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import declarative_base, Session, relationship
from datetime import datetime

# Подключение к БД
engine = create_engine('postgresql://user:password@localhost/mydb')
Base = declarative_base()

# Определение модели
class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    age = Column(Integer)
    created_at = Column(DateTime, default=datetime.now)
    
    # Связь с постами
    posts = relationship('Post', back_populates='author')

class Post(Base):
    __tablename__ = 'posts'
    
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    content = Column(String(1000))
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    created_at = Column(DateTime, default=datetime.now)
    
    # Связь с пользователем
    author = relationship('User', back_populates='posts')

# Создание таблиц
Base.metadata.create_all(engine)

# Примеры операций
with Session(engine) as session:
    # CREATE - создание
    user = User(name='John', email='john@example.com', age=30)
    session.add(user)
    session.commit()
    print(f'Created user with id: {user.id}')
    
    # READ - чтение
    user = session.query(User).filter(User.email == 'john@example.com').first()
    print(f'Found user: {user.name}')
    
    # UPDATE - обновление
    user.age = 31
    session.commit()
    print(f'Updated user age to {user.age}')
    
    # DELETE - удаление
    session.delete(user)
    session.commit()
    print('Deleted user')
    
    # СВЯЗЬ - работа с отношениями
    user = User(name='Jane', email='jane@example.com')
    post = Post(title='My First Post', content='Hello World', author=user)
    session.add(user)
    session.add(post)
    session.commit()
    
    # Чтение связанных данных
    user = session.query(User).filter(User.email == 'jane@example.com').first()
    print(f'User {user.name} has {len(user.posts)} posts')
    for post in user.posts:
        print(f'  - {post.title}')

Пример 2: Django ORM

# models.py
from django.db import models
from django.utils import timezone

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    age = models.IntegerField(null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    created_at = models.DateTimeField(default=timezone.now)
    
    def __str__(self):
        return self.title

# views.py или shell
from django.core.management.base import BaseCommand
from .models import User, Post

# CREATE
user = User.objects.create(name='John', email='john@example.com', age=30)
print(f'Created user with id: {user.id}')

# READ
user = User.objects.get(email='john@example.com')
print(f'Found user: {user.name}')

# UPDATE
user.age = 31
user.save()
print(f'Updated user age to {user.age}')

# DELETE
user.delete()
print('Deleted user')

# СВЯЗЬ
user = User.objects.create(name='Jane', email='jane@example.com')
post = Post.objects.create(title='My Post', content='Content', author=user)

user = User.objects.get(email='jane@example.com')
for post in user.posts.all():
    print(f'Post: {post.title}')

# ФИЛЬТРАЦИЯ
young_users = User.objects.filter(age__lt=30)  # age < 30
for u in young_users:
    print(u.name)

# СОРТИРОВКА
users = User.objects.all().order_by('-created_at')  # DESC

# АГРЕГАЦИЯ
from django.db.models import Q, Count
users_with_posts = User.objects.annotate(
    post_count=Count('posts')
).filter(post_count__gt=0)

Пример 3: Tortoise ORM (асинхронный)

from tortoise import Model, fields
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Tortoise

# Модели
class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=100, unique=True)
    age = fields.IntField(null=True)
    created_at = fields.DatetimeField(auto_now_add=True)
    
    # Связь
    posts: fields.ReverseRelation['Post']
    
    class Meta:
        table = 'users'

class Post(Model):
    id = fields.IntField(pk=True)
    title = fields.CharField(max_length=200)
    content = fields.TextField()
    author = fields.ForeignKeyField('models.User', related_name='posts')
    created_at = fields.DatetimeField(auto_now_add=True)
    
    class Meta:
        table = 'posts'

# Асинхронные операции
import asyncio

async def main():
    await Tortoise.init(
        db_url='postgres://user:password@localhost/mydb',
        modules={'models': ['__main__']}
    )
    await Tortoise.generate_schemas()
    
    # CREATE
    user = await User.create(name='John', email='john@example.com', age=30)
    print(f'Created user with id: {user.id}')
    
    # READ
    user = await User.get(email='john@example.com')
    print(f'Found user: {user.name}')
    
    # UPDATE
    user.age = 31
    await user.save()
    print(f'Updated user age to {user.age}')
    
    # DELETE
    await user.delete()
    print('Deleted user')
    
    # СВЯЗЬ
    user = await User.create(name='Jane', email='jane@example.com')
    post = await Post.create(
        title='My Post',
        content='Content',
        author=user
    )
    
    # Чтение связанных
    user_with_posts = await User.get(id=user.id).prefetch_related('posts')
    print(f'User {user_with_posts.name} has {len(user_with_posts.posts)} posts')
    
    # ЗАПРОСЫ
    young_users = await User.filter(age__lt=30)  # age < 30
    all_users = await User.all().order_by('-created_at')
    
    await Tortoise.close_connections()

if __name__ == '__main__':
    asyncio.run(main())

Сравнение операций

CREATE (создание)

# SQLAlchemy
user = User(name='John', email='john@example.com')
session.add(user)
session.commit()

# Django ORM
user = User.objects.create(name='John', email='john@example.com')

# Tortoise ORM (async)
user = await User.create(name='John', email='john@example.com')

READ (чтение)

# SQLAlchemy
user = session.query(User).filter(User.email == 'john@example.com').first()
all_users = session.query(User).all()

# Django ORM
user = User.objects.get(email='john@example.com')
all_users = User.objects.all()

# Tortoise ORM (async)
user = await User.get(email='john@example.com')
all_users = await User.all()

UPDATE (обновление)

# SQLAlchemy
user.age = 31
session.commit()

# Django ORM
user.age = 31
user.save()

# Tortoise ORM (async)
user.age = 31
await user.save()

DELETE (удаление)

# SQLAlchemy
session.delete(user)
session.commit()

# Django ORM
user.delete()

# Tortoise ORM (async)
await user.delete()

Преимущества ORM

# ✅ Не нужно писать SQL вручную
user = User.objects.get(id=1)

# ✅ Защита от SQL injection
user = User.objects.filter(name=user_input)  # Безопасно

# ✅ Переносимость между БД
# SQLAlchemy работает с PostgreSQL, MySQL, SQLite и т.д.
engine = create_engine('sqlite:///mydb.sqlite')
# Переключение: просто измени строку подключения

# ✅ Типизация и IDE поддержка
user = User.objects.get(id=1)
user.name  # IDE знает, что это строка

# ✅ Связи между таблицами
for post in user.posts.all():
    print(post.title)  # Автоматическое JOIN

Недостатки ORM

# ❌ Медленнее чем чистый SQL
users = User.objects.all()  # Может сделать лишний запрос

# ❌ Сложные запросы требуют знания ORM
complex_query = User.objects.filter(
    Q(age__gt=25) | Q(posts__count__gt=5)
).select_related('posts').prefetch_related('comments')

# ❌ N+1 problem
users = User.objects.all()
for user in users:
    print(user.posts.count())  # Запрос на каждого юзера!

# Решение: prefetch_related
users = User.objects.prefetch_related('posts')

Лучшие практики

class UserService:
    def __init__(self, session):
        self.session = session
    
    def get_user_with_posts(self, user_id: int):
        # ✅ Используй select_related для ForeignKey
        user = self.session.query(User).filter(User.id == user_id).first()
        
        # ✅ Используй prefetch_related для обратных связей
        user.posts  # Уже загружено
        
        return user
    
    def bulk_create(self, users_data: list):
        # ✅ Для массивных операций используй bulk_create
        users = [User(**data) for data in users_data]
        self.session.bulk_insert_mappings(User, users)
        self.session.commit()
    
    def get_active_users(self):
        # ✅ Фильтруй на уровне БД, не в Python
        return self.session.query(User).filter(User.age >= 18).all()

Вывод: ORM позволяет работать с БД как с объектами Python вместо написания SQL. Лучшие ORMs для Python: SQLAlchemy (универсальный), Django ORM (встроенный), Tortoise ORM (асинхронный). Используй ORM для стандартных операций, но не забывай о сложных SQL запросах.