To create an API using FastAPI for the many-to-many relationships in the models you've defined (such as User, Role,
Permission, and the junction tables UserRole and RolePermission), we'll first need to set up endpoints to handle CRUD
operations for these models. Specifically, we’ll provide the following functionalities:
1. Create and Retrieve Users, Roles, and Permissions
2. Assign Roles to Users
3. Assign Permissions to Roles
4. Get User Information along with their Roles
5. Get Role Information along with its Permissions
Here is an implementation of a basic FastAPI API based on your models:
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session
from uuid import UUID
from typing import List
from sqlmodel import Session, select
from models import User, Role, Permission, UserRole, RolePermission, OtpVerification
from database import engine, get_db # Assuming your DB engine and session are set up
app = FastAPI()
# Pydantic Schemas for validation and response formatting
class UserCreate(BaseModel):
user_name: str
email: str
password_hash: str
class RoleCreate(BaseModel):
name: str
class PermissionCreate(BaseModel):
name: str
class UserOut(BaseModel):
id: UUID
user_name: str
email: str
roles: List[str] # List of role names
class Config:
orm_mode = True
class RoleOut(BaseModel):
id: UUID
name: str
permissions: List[str] # List of permission names
class Config:
orm_mode = True
class PermissionOut(BaseModel):
id: UUID
name: str
class Config:
orm_mode = True
@app.post("/users/", response_model=UserOut)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = User(user_name=user.user_name, email=user.email, password_hash=user.password_hash)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@app.post("/roles/", response_model=RoleOut)
def create_role(role: RoleCreate, db: Session = Depends(get_db)):
db_role = Role(name=role.name)
db.add(db_role)
db.commit()
db.refresh(db_role)
return db_role
@app.post("/permissions/", response_model=PermissionOut)
def create_permission(permission: PermissionCreate, db: Session = Depends(get_db)):
db_permission = Permission(name=permission.name)
db.add(db_permission)
db.commit()
db.refresh(db_permission)
return db_permission
@app.post("/users/{user_id}/roles/{role_id}")
def assign_role_to_user(user_id: UUID, role_id: UUID, db: Session = Depends(get_db)):
db_user = db.get(User, user_id)
db_role = db.get(Role, role_id)
if db_user is None or db_role is None:
raise HTTPException(status_code=404, detail="User or Role not found")
user_role = UserRole(user_id=user_id, role_id=role_id)
db.add(user_role)
db.commit()
db.refresh(user_role)
return {"message": "Role assigned successfully"}
@app.post("/roles/{role_id}/permissions/{permission_id}")
def assign_permission_to_role(role_id: UUID, permission_id: UUID, db: Session = Depends(get_db)):
db_role = db.get(Role, role_id)
db_permission = db.get(Permission, permission_id)
if db_role is None or db_permission is None:
raise HTTPException(status_code=404, detail="Role or Permission not found")
role_permission = RolePermission(role_id=role_id, permission_id=permission_id)
db.add(role_permission)
db.commit()
db.refresh(role_permission)
return {"message": "Permission assigned successfully"}
@app.get("/users/{user_id}", response_model=UserOut)
def get_user(user_id: UUID, db: Session = Depends(get_db)):
db_user = db.get(User, user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.get("/roles/{role_id}", response_model=RoleOut)
def get_role(role_id: UUID, db: Session = Depends(get_db)):
db_role = db.get(Role, role_id)
if db_role is None:
raise HTTPException(status_code=404, detail="Role not found")
return db_role
@app.get("/permissions/{permission_id}", response_model=PermissionOut)
def get_permission(permission_id: UUID, db: Session = Depends(get_db)):
db_permission = db.get(Permission, permission_id)
if db_permission is None:
raise HTTPException(status_code=404, detail="Permission not found")
return db_permission
@app.get("/users/{user_id}/roles", response_model=List[RoleOut])
def get_user_roles(user_id: UUID, db: Session = Depends(get_db)):
statement = select(Role).join(UserRole).where(UserRole.user_id == user_id)
roles = db.exec(statement).all()
if not roles:
raise HTTPException(status_code=404, detail="No roles found for this user")
return roles
@app.get("/roles/{role_id}/permissions", response_model=List[PermissionOut])
def get_role_permissions(role_id: UUID, db: Session = Depends(get_db)):
statement = select(Permission).join(RolePermission).where(RolePermission.role_id == role_id)
permissions = db.exec(statement).all()
if not permissions:
raise HTTPException(status_code=404, detail="No permissions found for this role")
return permissions
# Database engine setup and session dependency
from sqlmodel import SQLModel, create_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(DATABASE_URL)
SQLModel.metadata.create_all(bind=engine)
def get_db():
db = sessionmaker(autocommit=False, autoflush=False, bind=engine)()
try:
yield db
finally:
db.close()
Key Points in this Code:
1. CRUD Endpoints: We created routes for creating User, Role, and Permission.
POST /users/ creates a new user.
POST /roles/ creates a new role.
POST /permissions/ creates a new permission.
2. Many-to-Many Assignments: We have routes to:
Assign a Role to a User (via POST /users/{user_id}/roles/{role_id}).
Assign a Permission to a Role (via POST /roles/{role_id}/permissions/{permission_id}).
3. Relationship Lookups: We provide routes to fetch the roles of a user and permissions of a role:
GET /users/{user_id}/roles returns the roles assigned to a user.
GET /roles/{role_id}/permissions returns the permissions assigned to a role.
4. Pydantic Models: The Pydantic models like UserOut, RoleOut, and PermissionOut help define what will be
returned in API responses.
Testing the API
You can test the API using curl, Postman, or Swagger UI. FastAPI automatically generates Swagger UI for your
endpoints at http://localhost:8000/docs.
Make sure you have the necessary database setup and an active connection to the PostgreSQL database. You can
replace the DATABASE_URL with your actual connection string.