Files
TBank-Backend/docs/user-guide/database/index.md
2025-10-19 22:09:35 +03:00

235 lines
6.3 KiB
Markdown

# Database Layer
Learn how to work with the database layer in the FastAPI Boilerplate. This section covers everything you need to store and retrieve data effectively.
## What You'll Learn
- **[Models](models.md)** - Define database tables with SQLAlchemy models
- **[Schemas](schemas.md)** - Validate and serialize data with Pydantic schemas
- **[CRUD Operations](crud.md)** - Perform database operations with FastCRUD
- **[Migrations](migrations.md)** - Manage database schema changes with Alembic
## Quick Overview
The boilerplate uses a layered architecture that separates concerns:
```python
# API Endpoint
@router.post("/", response_model=UserRead)
async def create_user(user_data: UserCreate, db: AsyncSession):
return await crud_users.create(db=db, object=user_data)
# The layers work together:
# 1. UserCreate schema validates the input
# 2. crud_users handles the database operation
# 3. User model defines the database table
# 4. UserRead schema formats the response
```
## Architecture
The database layer follows a clear separation:
```
API Request
Pydantic Schema (validation & serialization)
CRUD Layer (business logic & database operations)
SQLAlchemy Model (database table definition)
PostgreSQL Database
```
## Key Features
### 🗄️ **SQLAlchemy 2.0 Models**
Modern async SQLAlchemy with type hints:
```python
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(String(50), unique=True)
email: Mapped[str] = mapped_column(String(100), unique=True)
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
```
### ✅ **Pydantic Schemas**
Automatic validation and serialization:
```python
class UserCreate(BaseModel):
username: str = Field(min_length=2, max_length=50)
email: EmailStr
password: str = Field(min_length=8)
class UserRead(BaseModel):
id: int
username: str
email: str
created_at: datetime
# Note: no password field in read schema
```
### 🔧 **FastCRUD Operations**
Consistent database operations:
```python
# Create
user = await crud_users.create(db=db, object=user_create)
# Read
user = await crud_users.get(db=db, id=user_id)
users = await crud_users.get_multi(db=db, offset=0, limit=10)
# Update
user = await crud_users.update(db=db, object=user_update, id=user_id)
# Delete (soft delete)
await crud_users.delete(db=db, id=user_id)
```
### 🔄 **Database Migrations**
Track schema changes with Alembic:
```bash
# Generate migration
alembic revision --autogenerate -m "Add user table"
# Apply migrations
alembic upgrade head
# Rollback if needed
alembic downgrade -1
```
## Database Setup
The boilerplate is configured for PostgreSQL with async support:
### Environment Configuration
```bash
# .env file
POSTGRES_USER=your_user
POSTGRES_PASSWORD=your_password
POSTGRES_SERVER=localhost
POSTGRES_PORT=5432
POSTGRES_DB=your_database
```
### Connection Management
```python
# Database session dependency
async def async_get_db() -> AsyncIterator[AsyncSession]:
async with async_session_maker() as session:
yield session
# Use in endpoints
@router.get("/users/")
async def get_users(db: Annotated[AsyncSession, Depends(async_get_db)]):
return await crud_users.get_multi(db=db)
```
## Included Models
The boilerplate includes four example models:
### **User Model** - Authentication & user management
- Username, email, password (hashed)
- Soft delete support
- Tier-based access control
### **Post Model** - Content with user relationships
- Title, content, creation metadata
- Foreign key to user (no SQLAlchemy relationships)
- Soft delete built-in
### **Tier Model** - User subscription levels
- Name-based tiers (free, premium, etc.)
- Links to rate limiting system
### **Rate Limit Model** - API access control
- Path-specific rate limits per tier
- Configurable limits and time periods
## Directory Structure
```text
src/app/
├── models/ # SQLAlchemy models (database tables)
│ ├── __init__.py
│ ├── user.py # User table definition
│ ├── post.py # Post table definition
│ └── ...
├── schemas/ # Pydantic schemas (validation)
│ ├── __init__.py
│ ├── user.py # User validation schemas
│ ├── post.py # Post validation schemas
│ └── ...
├── crud/ # Database operations
│ ├── __init__.py
│ ├── crud_users.py # User CRUD operations
│ ├── crud_posts.py # Post CRUD operations
│ └── ...
└── core/db/ # Database configuration
├── database.py # Connection and session setup
└── models.py # Base classes and mixins
```
## Common Patterns
### Create with Validation
```python
@router.post("/users/", response_model=UserRead)
async def create_user(
user_data: UserCreate, # Validates input automatically
db: Annotated[AsyncSession, Depends(async_get_db)]
):
# Check for duplicates
if await crud_users.exists(db=db, email=user_data.email):
raise DuplicateValueException("Email already exists")
# Create user (password gets hashed automatically)
return await crud_users.create(db=db, object=user_data)
```
### Query with Filters
```python
# Get active users only
users = await crud_users.get_multi(
db=db,
is_active=True,
is_deleted=False,
offset=0,
limit=10
)
# Search users
users = await crud_users.get_multi(
db=db,
username__icontains="john", # Contains "john"
schema_to_select=UserRead
)
```
### Soft Delete Pattern
```python
# Soft delete (sets is_deleted=True)
await crud_users.delete(db=db, id=user_id)
# Hard delete (actually removes from database)
await crud_users.db_delete(db=db, id=user_id)
# Get only non-deleted records
users = await crud_users.get_multi(db=db, is_deleted=False)
```
## What's Next
Each guide builds on the previous one with practical examples:
1. **[Models](models.md)** - Define your database structure
2. **[Schemas](schemas.md)** - Add validation and serialization
3. **[CRUD Operations](crud.md)** - Implement business logic
4. **[Migrations](migrations.md)** - Deploy changes safely
The boilerplate provides a solid foundation - just follow these patterns to build your data layer!