Skip to content

Commit 5985660

Browse files
committed
crud app with FastAPI and SQLAlchemy
0 parents  commit 5985660

9 files changed

Lines changed: 196 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__pycache__
2+
venv/
3+
# .env
4+
.db

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
dev:
2+
docker-compose up -d
3+
4+
dev-down:
5+
docker-compose down
6+
7+
server:
8+
uvicorn app.main:app --reload

app/__init__.py

Whitespace-only changes.

app/database.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from sqlalchemy import create_engine
2+
from sqlalchemy.ext.declarative import declarative_base
3+
from sqlalchemy.orm import sessionmaker
4+
5+
engine = create_engine(
6+
"sqlite:///./note.db", echo=True, connect_args={"check_same_thread": False}
7+
)
8+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
9+
10+
Base = declarative_base()
11+
12+
13+
def get_db():
14+
db = SessionLocal()
15+
try:
16+
yield db
17+
finally:
18+
db.close()

app/main.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from app import models, note
2+
from fastapi import FastAPI
3+
from fastapi.middleware.cors import CORSMiddleware
4+
from .database import engine
5+
6+
models.Base.metadata.create_all(bind=engine)
7+
8+
app = FastAPI()
9+
10+
origins = [
11+
"http://localhost:3000/",
12+
]
13+
14+
app.add_middleware(
15+
CORSMiddleware,
16+
allow_origins=origins,
17+
allow_credentials=True,
18+
allow_methods=["*"],
19+
allow_headers=["*"],
20+
)
21+
22+
23+
app.include_router(note.router, tags=['Notes'], prefix='/api/notes')
24+
25+
26+
@app.get("/api/healthchecker")
27+
def root():
28+
return {"message": "Welcome to FastAPI with SQLAlchemy"}

app/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from .database import Base
2+
from sqlalchemy import TIMESTAMP, Column, String, Boolean
3+
from sqlalchemy.sql import func
4+
from fastapi_utils.guid_type import GUID, GUID_DEFAULT_SQLITE
5+
6+
7+
class Note(Base):
8+
__tablename__ = 'notes'
9+
id = Column(GUID, primary_key=True, default=GUID_DEFAULT_SQLITE)
10+
title = Column(String, nullable=False)
11+
content = Column(String, nullable=False)
12+
category = Column(String, nullable=True)
13+
published = Column(Boolean, nullable=False, default=True)
14+
createdAt = Column(TIMESTAMP(timezone=True),
15+
nullable=False, server_default=func.now())
16+
updatedAt = Column(TIMESTAMP(timezone=True),
17+
default=None, onupdate=func.now())

app/note.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from datetime import datetime
2+
from . import schemas, models
3+
from sqlalchemy.orm import Session
4+
from fastapi import Depends, HTTPException, status, APIRouter, Response
5+
from .database import get_db
6+
7+
router = APIRouter()
8+
9+
10+
@router.get('/')
11+
def get_notes(db: Session = Depends(get_db), limit: int = 10, page: int = 1, search: str = ''):
12+
skip = (page - 1) * limit
13+
14+
notes = db.query(models.Note).group_by(models.Note.id).filter(
15+
models.Note.title.contains(search)).limit(limit).offset(skip).all()
16+
return {'status': 'success', 'results': len(notes), 'notes': notes}
17+
18+
19+
@router.post('/', status_code=status.HTTP_201_CREATED)
20+
def create_note(payload: schemas.NoteBaseSchema, db: Session = Depends(get_db)):
21+
new_note = models.Note(**payload.dict())
22+
db.add(new_note)
23+
db.commit()
24+
db.refresh(new_note)
25+
return {"status": "success", "note": new_note}
26+
27+
28+
@router.patch('/{noteId}')
29+
def update_note(noteId: str, payload: schemas.NoteBaseSchema, db: Session = Depends(get_db)):
30+
note_query = db.query(models.Note).filter(models.Note.id == noteId)
31+
db_note = note_query.first()
32+
33+
if not db_note:
34+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
35+
detail=f'No note with this id: {id} found')
36+
update_data = payload.dict(exclude_unset=True)
37+
# Work Here
38+
note_query.update(db_note,
39+
synchronize_session=False)
40+
db.commit()
41+
return {"status": "success", "note": db_note}
42+
43+
44+
@router.get('/{noteId}')
45+
def get_post(noteId: str, db: Session = Depends(get_db)):
46+
note = db.query(models.Note).filter(models.Note.id == noteId).first()
47+
if not note:
48+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
49+
detail=f"No note with this id: {id} found")
50+
return {"status": "success", "note": note}
51+
52+
53+
@router.delete('/{noteId}')
54+
def delete_post(noteId: str, db: Session = Depends(get_db)):
55+
note_query = db.query(models.Note).filter(models.Note.id == noteId)
56+
note = note_query.first()
57+
if not note:
58+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
59+
detail=f'No note with this id: {id} found')
60+
note_query.delete(synchronize_session=False)
61+
db.commit()
62+
return Response(status_code=status.HTTP_204_NO_CONTENT)

app/schemas.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from datetime import datetime
2+
from typing import List
3+
from pydantic import BaseModel
4+
5+
6+
class NoteBaseSchema(BaseModel):
7+
id: str | None = None
8+
title: str
9+
content: str
10+
category: str | None = None
11+
published: bool = False
12+
createdAt: datetime | None = None
13+
updatedAt: datetime | None = None
14+
15+
class Config:
16+
orm_mode = True
17+
allow_population_by_field_name = True
18+
arbitrary_types_allowed = True
19+
20+
21+
class ListNoteResponse(BaseModel):
22+
status: str
23+
results: int
24+
notes: List[NoteBaseSchema]

requirements.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
anyio==3.6.2
2+
autopep8==1.7.1
3+
certifi==2022.9.24
4+
charset-normalizer==2.1.1
5+
click==8.1.3
6+
colorama==0.4.6
7+
dnspython==2.2.1
8+
email-validator==1.3.0
9+
fastapi==0.85.1
10+
fastapi-utils==0.2.1
11+
greenlet==1.1.3.post0
12+
h11==0.14.0
13+
httptools==0.5.0
14+
idna==3.4
15+
itsdangerous==2.1.2
16+
Jinja2==3.1.2
17+
MarkupSafe==2.1.1
18+
orjson==3.8.1
19+
pycodestyle==2.9.1
20+
pydantic==1.10.2
21+
python-dotenv==0.21.0
22+
python-multipart==0.0.5
23+
PyYAML==6.0
24+
requests==2.28.1
25+
six==1.16.0
26+
sniffio==1.3.0
27+
SQLAlchemy==1.4.42
28+
starlette==0.20.4
29+
tomli==2.0.1
30+
typing_extensions==4.4.0
31+
ujson==5.5.0
32+
urllib3==1.26.12
33+
uvicorn==0.18.3
34+
watchfiles==0.18.0
35+
websockets==10.4

0 commit comments

Comments
 (0)