python-backend

Expert-level Python backend patterns for FastAPI, Django, Flask, and SQLAlchemy. Use this skill for building REST APIs, database patterns, dependency injection, and backend architecture.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "python-backend" with this command: npx skills add amrahman90/python-expert-agent/amrahman90-python-expert-agent-python-backend

Python Backend

Expert-level Python backend patterns for FastAPI, Django, Flask, and SQLAlchemy. Use this skill for building REST APIs, database patterns, dependency injection, and backend architecture.

When to Use This Skill

  • Building REST APIs with FastAPI or Django

  • Working with SQLAlchemy or Django ORM

  • Implementing dependency injection

  • Designing API endpoints and versioning

  • Handling authentication and authorization

  • Database optimization patterns

FastAPI Patterns

Pydantic Models

from pydantic import BaseModel, EmailStr, Field, field_validator

class UserCreate(BaseModel): email: EmailStr name: str = Field(..., min_length=2, max_length=100) password: str = Field(..., min_length=8)

@field_validator('name')
@classmethod
def name_must_not_be_empty(cls, v: str) -> str:
    if not v.strip():
        raise ValueError('Name cannot be empty')
    return v.strip()

model_config = {'str_strip_whitespace': True}

Dependency Injection

from typing import Annotated from fastapi import Depends, HTTPException

async def get_current_user( token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db) ) -> User: user = await verify_token(token, db) if user is None: raise HTTPException(status_code=401, detail="Invalid token") return user

CurrentUser = Annotated[User, Depends(get_current_user)] DB = Annotated[AsyncSession, Depends(get_db)]

@app.get("/me") async def get_me(user: CurrentUser, db: DB): return user

Background Tasks

from fastapi import BackgroundTasks

@app.post("/users/") async def create_user( user: UserCreate, background_tasks: BackgroundTasks, db: AsyncSession = Depends(get_db) ): db_user = await crud.create_user(db, user) background_tasks.add_task(send_welcome_email, user.email) return db_user

Exception Handling

from fastapi import HTTPException from fastapi.responses import JSONResponse

class AppException(Exception): def init(self, code: str, message: str, status_code: int = 400): self.code = code self.message = message self.status_code = status_code

@app.exception_handler(AppException) async def app_exception_handler(request, exc: AppException): return JSONResponse( status_code=exc.status_code, content={"code": exc.code, "message": exc.message} )

Lifespan Events

from contextlib import asynccontextmanager

@asynccontextmanager async def lifespan(app: FastAPI): # Startup await database.connect() yield # Shutdown await database.disconnect()

app = FastAPI(lifespan=lifespan)

SQLAlchemy 2.0 Patterns

Modern Declarative Syntax

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship from sqlalchemy import ForeignKey

class Base(DeclarativeBase): pass

class User(Base): tablename = "users"

id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(unique=True, index=True)
name: Mapped[str]
is_active: Mapped[bool] = mapped_column(default=True)

posts: Mapped[list["Post"]] = relationship(back_populates="author")

Async Queries

from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select

async def get_user(db: AsyncSession, user_id: int) -> User | None: stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) return result.scalar_one_or_none()

Eager Loading

from sqlalchemy.orm import selectinload, joinedload

Prevent N+1 queries

stmt = select(User).options( selectinload(User.posts), # Separate query joinedload(User.profile) # JOIN )

Pagination

from sqlalchemy import func, select

async def get_users_paginated( db: AsyncSession, page: int = 1, per_page: int = 20 ) -> tuple[list[User], int]: # Count total count_stmt = select(func.count()).select_from(User) total = await db.scalar(count_stmt)

# Get page
stmt = select(User).offset((page - 1) * per_page).limit(per_page)
result = await db.execute(stmt)
users = result.scalars().all()

return users, total

Django Patterns

Prevent N+1 Queries

BAD - N+1 queries

users = User.objects.all() for user in users: print(user.profile.bio) # 1 query per user!

GOOD - select_related for FK

users = User.objects.select_related('profile').all()

GOOD - prefetch_related for M2M/reverse FK

posts = Post.objects.prefetch_related('tags', 'comments').all()

Atomic Updates

from django.db.models import F

BAD - Race condition

article = Article.objects.get(pk=1) article.views += 1 article.save()

GOOD - Atomic update

Article.objects.filter(pk=1).update(views=F('views') + 1)

Complex Queries

from django.db.models import Q

OR query

users = User.objects.filter( Q(is_staff=True) | Q(is_superuser=True) )

Combined conditions

users = User.objects.filter( Q(email__endswith='@company.com') & (Q(is_active=True) | Q(is_staff=True)) )

Efficient Checks

BAD - Loads all objects

if len(User.objects.filter(email=email)) > 0: ...

GOOD - Efficient existence check

if User.objects.filter(email=email).exists(): ...

Bulk Operations

BAD - N queries

for user_data in users_data: User.objects.create(**user_data)

GOOD - 1 query

User.objects.bulk_create([ User(**data) for data in users_data ])

Transactions

from django.db import transaction

@transaction.atomic def transfer_funds(from_account, to_account, amount): from_account.balance = F('balance') - amount from_account.save() to_account.balance = F('balance') + amount to_account.save()

API Design

RESTful Conventions

Method Endpoint Purpose Response

GET /users List all 200 + array

GET /users/:id Get one 200 / 404

POST /users Create 201 + created

PUT /users/:id Replace 200 / 404

PATCH /users/:id Update 200 / 404

DELETE /users/:id Delete 204 / 404

Nested Resources

GET /users/:userId/orders # User's orders POST /users/:userId/orders # Create order for user GET /users/:userId/orders/:orderId # Specific order

Naming Conventions

Type Convention Example

Resources Plural nouns /users , /orders

Actions Verbs (rare) /auth/login , /reports/generate

Query params snake_case ?page_size=20

Request/Response camelCase { "firstName": "John" }

Versioning Strategies

Strategy Example When to Use

URL Path /v1/users

Most common, clear

Header Accept: application/vnd.api+json;version=1

Clean URLs

Query /users?version=1

Simple but messy

Recommended: URL path (/api/v1/... )

Response Format

Success

{ "data": { "id": "123", "name": "John" }, "meta": { "timestamp": "2025-01-01T00:00:00Z" } }

List with Pagination

{ "data": [{ "id": "1" }, { "id": "2" }], "meta": { "page": 1, "pageSize": 20, "total": 100, "totalPages": 5 } }

Error

{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid input", "fields": { "email": "Invalid format" } } }

Status Codes

Code Meaning When to Use

200 OK Successful GET/PUT/PATCH

201 Created Successful POST

204 No Content Successful DELETE

400 Bad Request Invalid input

401 Unauthorized No/invalid auth

403 Forbidden No permission

404 Not Found Resource missing

409 Conflict Duplicate/state conflict

422 Unprocessable Validation failed

429 Too Many Rate limited

500 Server Error Unexpected error

Pagination Patterns

Offset-based (Simple)

GET /users?page=2&page_size=20

Cursor-based (Scalable)

GET /users?cursor=eyJpZCI6MTAwfQ&limit=20

Pattern Pros Cons

Offset Simple, jump to page Slow on large datasets

Cursor Consistent, fast No page jumping

Filtering & Sorting

Filter

GET /users?status=active&role=admin

Sort

GET /users?sort=created_at:desc,name:asc

Search

GET /users?q=john

Combined

GET /users?status=active&sort=name:asc&page=1&page_size=20

API Documentation (OpenAPI)

openapi: 3.0.0 info: title: User API version: 1.0.0 paths: /users: get: summary: List users parameters: - name: page in: query schema: { type: integer, default: 1 } responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/UserList'

Result Pattern (No Exceptions in Core)

from dataclasses import dataclass from typing import Generic, TypeVar

T = TypeVar('T') E = TypeVar('E')

@dataclass class Ok(Generic[T]): value: T

@dataclass class Err(Generic[E]): error: E

Result = Ok[T] | Err[E]

async def get_user(user_id: int) -> Result[User, str]: user = await db.get(User, user_id) if user is None: return Err("User not found") return Ok(user)

Usage

match await get_user(123): case Ok(user): print(f"Found: {user.name}") case Err(error): print(f"Error: {error}")

Best Practices

Do's

  • Use plural nouns for resources

  • Return created/updated resource

  • Include pagination metadata

  • Document with OpenAPI

  • Version from day one

  • Use Pydantic for validation

  • Prefer async for I/O operations

Don'ts

  • Use verbs in resource names

  • Return different formats per endpoint

  • Expose internal IDs/structures

  • Ignore backward compatibility

  • Skip error code standards

  • Use bare except clauses

References

  • FastAPI Documentation

  • SQLAlchemy 2.0 Documentation

  • Django Documentation

  • OpenAPI Specification

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-asyncio

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-package-management

No summary provided by upstream source.

Repository SourceNeeds Review