diff --git a/.gitignore b/.gitignore index 033df5f..3b88faa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -.venv -__pycache__ +.venv/ +__pycache__/ +.mypy_cache/ diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/README.md b/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/README.md new file mode 100644 index 0000000..294a9b3 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/README.md @@ -0,0 +1,19 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/ch1/requirements.txt b/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/ch1/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/1.Setting Up the Development Environment/ch1/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/README.md b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/README.md new file mode 100644 index 0000000..dbaa49d --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/README.md @@ -0,0 +1,35 @@ +- [Run FastAPI Doc](https://fastapi.tiangolo.com/#run-it) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/main.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/main.py new file mode 100644 index 0000000..7a8da18 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def home(): + return {"message": "Hello Fast API"} diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/requirements.txt b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/2.Your First FastAPI App/ch2/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/README.md b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/README.md new file mode 100644 index 0000000..dbaa49d --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/README.md @@ -0,0 +1,35 @@ +- [Run FastAPI Doc](https://fastapi.tiangolo.com/#run-it) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/.env b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/.env new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/__init__.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/db/__init__.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/db/config.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/db/config.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/main.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/main.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/__init__.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/models.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/models.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/routes.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/routes.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/schemas.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/schemas.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/services.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/product/services.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/__init__.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/models.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/models.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/routes.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/routes.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/schemas.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/schemas.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/services.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/app/user/services.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/requirements.txt b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/__init__.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_models.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_models.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_routes.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_routes.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_services.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/3.FastAPI Clean Folder Structure/ch3/tests/user/test_services.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/README.md b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/README.md new file mode 100644 index 0000000..32b8481 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/README.md @@ -0,0 +1,41 @@ +- [Run FastAPI Doc](https://fastapi.tiangolo.com/#run-it) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/app/main.py b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/app/main.py new file mode 100644 index 0000000..df7506f --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/app/main.py @@ -0,0 +1,7 @@ +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/") +def home(): + return {"message": "Hello Fast API"} \ No newline at end of file diff --git a/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/requirements.txt b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-1.Introduction to Fast API and Setup/4.Auto-Generated FastAPI App Documentation/ch4/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/1.png b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/1.png new file mode 100644 index 0000000..3bc62aa Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/1.png differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/README.md new file mode 100644 index 0000000..07d371f --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/1.Introduction to SQLAlchemy/README.md @@ -0,0 +1,3 @@ +- [SQLAlchemy Official Doc](https://www.sqlalchemy.org/) + +- ![Introduction to SQLAlchemy](1.png) diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/1.png b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/1.png new file mode 100644 index 0000000..8fd1f3a Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/1.png differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/2.png b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/2.png new file mode 100644 index 0000000..c0fcc0b Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/2.SQLAlchemy APIs/2.png differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/README.md new file mode 100644 index 0000000..79e0abb --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Installation Doc](https://docs.sqlalchemy.org/en/20/intro.html#installation) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/3.Install SQLAlchemy and Create Engine/ch35/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/main.py new file mode 100644 index 0000000..3cf0d84 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/main.py @@ -0,0 +1,4 @@ +from tables import create_tables + +# Create Tables +create_tables() \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/sqlite.db new file mode 100644 index 0000000..5fa006f Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/tables.py new file mode 100644 index 0000000..3d37a34 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/4.Create Database Tables/ch36/tables.py @@ -0,0 +1,32 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String + +metadata = MetaData() + +# User Table +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True), + Column("phone", Integer, nullable=False, unique=True) + ) + +# address Table +address = Table( + "address", + metadata, + Column("id", Integer, primary_key=True), + Column("street", String(length=50), nullable=False), + Column("dist", String, nullable=False, unique=True), + Column("country", String, nullable=False, unique=True) + ) + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) + +# # Drop Table in Database +# def drop_tables(): +# metadata.drop_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/main.py new file mode 100644 index 0000000..3cf0d84 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/main.py @@ -0,0 +1,4 @@ +from tables import create_tables + +# Create Tables +create_tables() \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/sqlite.db new file mode 100644 index 0000000..04f3d8a Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/tables.py new file mode 100644 index 0000000..eede983 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/5.One to One, One to Many and Many to Many Relationship/ch37/tables.py @@ -0,0 +1,56 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey + +metadata = MetaData() + +# User Table +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True), + Column("phone", Integer, nullable=False, unique=True) + ) + +# One to Many +posts = Table( + "posts", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False), + Column("title", String, nullable=False), + Column("content", String, nullable=False), +) + +# One to One +profile = Table( + "profile", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True), + Column("bio", String, nullable=False), + Column("address", String, nullable=False), +) + +# Many to Many +address = Table( + "address", + metadata, + Column("id", Integer, primary_key=True), + Column("street", String, nullable=False), + Column("country", String, nullable=False), +) + +user_address_association = Table( + "user_address_association", + metadata, + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), + Column("address_id", Integer, ForeignKey("address.id", ondelete="CASCADE"), primary_key=True), +) + + + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/main.py new file mode 100644 index 0000000..be712cf --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/main.py @@ -0,0 +1,22 @@ +from tables import create_tables +from services import * + +# Create Tables +create_tables() + +# Create Data +# create_user("sonam", "sonam@example.com") +# create_user("raj", "raj@example.com") +# create_post(1, "Hello World", "This is Sonam's first post") +# create_post(2, "Raj's Post", "Hi from Raj!") + +# Read data +# print(get_user_by_id(1)) +# print(get_all_users()) +# print(get_posts_by_user(2)) + +# Update data +# update_user_email(1, "sonam@newdomain.com") + +# Delete Data +# delete_post(2) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/services.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/services.py new file mode 100644 index 0000000..ad9e640 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/services.py @@ -0,0 +1,52 @@ +from db import engine +from tables import users, posts +from sqlalchemy import insert, select, update, delete + +# Insert or Create User +def create_user(name: str, email: str): + with engine.connect() as conn: + stmt = insert(users).values(name=name, email=email) + conn.execute(stmt) + conn.commit() + +# Insert or Create Post +def create_post(user_id: int, title: str, content: str): + with engine.connect() as conn: + stmt = insert(posts).values(user_id=user_id, title=title, content=content) + conn.execute(stmt) + conn.commit() + +# Get Single User by ID +def get_user_by_id(user_id: int): + with engine.connect() as conn: + stmt = select(users).where(users.c.id == user_id) + result = conn.execute(stmt).first() + return result + +# Get All Users +def get_all_users(): + with engine.connect() as conn: + stmt = select(users) + result = conn.execute(stmt).fetchall() + return result + +# Get Post by User +def get_posts_by_user(user_id: int): + with engine.connect() as conn: + stmt = select(posts).where(posts.c.user_id == user_id) + result = conn.execute(stmt).fetchall() + return result + +# Update User Email +def update_user_email(user_id: int, new_email: str): + with engine.connect() as conn: + stmt = update(users).where(users.c.id == user_id).values(email=new_email) + conn.execute(stmt) + conn.commit() + +# Delete Post +def delete_post(post_id: int): + with engine.connect() as conn: + stmt = delete(posts).where(posts.c.id == post_id) + conn.execute(stmt) + conn.commit() \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/sqlite.db new file mode 100644 index 0000000..64118d8 Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/tables.py new file mode 100644 index 0000000..9bc9ae9 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/6.CRUD/ch38/tables.py @@ -0,0 +1,25 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) + +posts = Table( + "posts", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False), + Column("title", String, nullable=False), + Column("content", String, nullable=False), +) + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/main.py new file mode 100644 index 0000000..5e6084e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/main.py @@ -0,0 +1,21 @@ +from tables import create_tables +from services import * + +# Create Tables +create_tables() + +# Create Data +# create_user("sonam", "sonam@example.com") +# create_user("raj", "raj@example.com") +# create_post(1, "Hello World", "This is Sonam's first post") +# create_post(1, "Bye World", "This is Sonam's second post") +# create_post(1, "No World", "This is Sonam's third post") +# create_post(2, "Raj's Post 1", "Hi from Raj! 1") +# create_post(2, "Raj's Post 2", "Hi from Raj! 2") + +# Order By +# print(get_users_ordered_by_name()) +# print(get_posts_latest_first()) + +# Group By +# print(get_post_count_per_user()) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/services.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/services.py new file mode 100644 index 0000000..08f3864 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/services.py @@ -0,0 +1,41 @@ +from db import engine +from tables import users, posts +from sqlalchemy import insert, select, asc, desc, func + +# Insert or Create User +def create_user(name: str, email: str): + with engine.connect() as conn: + stmt = insert(users).values(name=name, email=email) + conn.execute(stmt) + conn.commit() + +# Insert or Create Post +def create_post(user_id: int, title: str, content: str): + with engine.connect() as conn: + stmt = insert(posts).values(user_id=user_id, title=title, content=content) + conn.execute(stmt) + conn.commit() + +# Get All Users Ordered by Name (A-Z) +def get_users_ordered_by_name(): + with engine.connect() as conn: + stmt = select(users).order_by(asc(users.c.name)) + result = conn.execute(stmt).fetchall() + return result + +# Get All Posts Ordered by Latest +def get_posts_latest_first(): + with engine.connect() as conn: + stmt = select(posts).order_by(desc(posts.c.id)) + result = conn.execute(stmt).fetchall() + return result + +# Group Posts by User (Count how many posts each user has) +def get_post_count_per_user(): + with engine.connect() as conn: + stmt = select( + posts.c.user_id, + func.count(posts.c.id).label("total_posts") + ).group_by(posts.c.user_id) + result = conn.execute(stmt).fetchall() + return result \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/sqlite.db new file mode 100644 index 0000000..9c8d3f7 Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/tables.py new file mode 100644 index 0000000..9bc9ae9 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/7.Order_by and Group_by/ch39/tables.py @@ -0,0 +1,25 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) + +posts = Table( + "posts", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False), + Column("title", String, nullable=False), + Column("content", String, nullable=False), +) + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/main.py new file mode 100644 index 0000000..50566f9 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/main.py @@ -0,0 +1,16 @@ +from tables import create_tables +from services import * + +# Create Tables +create_tables() + +# Create Data +# create_user("sonam", "sonam@example.com") +# create_user("raj", "raj@example.com") +# create_post(1, "Hello World", "This is Sonam's first post") +# create_post(1, "Bye World", "This is Sonam's second post") +# create_post(1, "No World", "This is Sonam's third post") +# create_post(2, "Raj's Post 1", "Hi from Raj! 1") +# create_post(2, "Raj's Post 2", "Hi from Raj! 2") + +print(get_posts_with_author()) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/services.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/services.py new file mode 100644 index 0000000..f8eac44 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/services.py @@ -0,0 +1,28 @@ +from db import engine +from tables import users, posts +from sqlalchemy import insert, select + +# Insert or Create User +def create_user(name: str, email: str): + with engine.connect() as conn: + stmt = insert(users).values(name=name, email=email) + conn.execute(stmt) + conn.commit() + +# Insert or Create Post +def create_post(user_id: int, title: str, content: str): + with engine.connect() as conn: + stmt = insert(posts).values(user_id=user_id, title=title, content=content) + conn.execute(stmt) + conn.commit() + +# Join Users and Posts (List all posts with author names) +def get_posts_with_author(): + with engine.connect() as conn: + stmt = select( + posts.c.id, + posts.c.title, + users.c.name.label("author_name") + ).join(users, posts.c.user_id == users.c.id) + result = conn.execute(stmt).fetchall() + return result \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/sqlite.db new file mode 100644 index 0000000..9c8d3f7 Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/tables.py new file mode 100644 index 0000000..9bc9ae9 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/8.Join/ch40/tables.py @@ -0,0 +1,25 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) + +posts = Table( + "posts", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False), + Column("title", String, nullable=False), + Column("content", String, nullable=False), +) + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/README.md b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/README.md new file mode 100644 index 0000000..4e05d2e --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy Core Doc](https://docs.sqlalchemy.org/en/20/core/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/db.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/db.py new file mode 100644 index 0000000..759a88a --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/db.py @@ -0,0 +1,3 @@ +from sqlalchemy import create_engine +DATABASE_URL = "sqlite:///./sqlite.db" +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/main.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/main.py new file mode 100644 index 0000000..4056926 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/main.py @@ -0,0 +1,9 @@ +from tables import create_tables +from services import * + +# Create Tables +create_tables() + + +# raw_sql_insert() +print(raw_sql_example()) \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/requirements.txt b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/services.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/services.py new file mode 100644 index 0000000..bfec733 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/services.py @@ -0,0 +1,20 @@ +from db import engine +from tables import users, posts +from sqlalchemy import text + +# Using RAW SQL (Insert) +def raw_sql_insert(): + with engine.connect() as conn: + stmt = text(""" + INSERT INTO users (name, email) + VALUES (:name, :email) + """) + conn.execute(stmt, {"name": "sonam", "email":"sonam@example.com"}) + conn.commit() + +# Using RAW SQL (SELECT) +def raw_sql_example(): + with engine.connect() as conn: + stmt = text("SELECT * FROM users WHERE email = :email") + result = conn.execute(stmt, {"email": "sonam@example.com"}).first() + return result \ No newline at end of file diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/sqlite.db b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/sqlite.db new file mode 100644 index 0000000..1e9f6a1 Binary files /dev/null and b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/sqlite.db differ diff --git a/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/tables.py b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/tables.py new file mode 100644 index 0000000..9bc9ae9 --- /dev/null +++ b/Geekyshows/Section-10.Working with Databases Using SQLAlchemy Core/9.Raw SQL/ch41/tables.py @@ -0,0 +1,25 @@ +from db import engine +from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) + +posts = Table( + "posts", + metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False), + Column("title", String, nullable=False), + Column("content", String, nullable=False), +) + +# Create Table in Database +def create_tables(): + metadata.create_all(engine) diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/README.md b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/README.md new file mode 100644 index 0000000..8567111 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy ORM Doc](https://docs.sqlalchemy.org/en/20/orm/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/db.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/requirements.txt b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/1.Install and Setup SQLAlchemy ORM/ch42/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/README.md b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/README.md new file mode 100644 index 0000000..8567111 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy ORM Doc](https://docs.sqlalchemy.org/en/20/orm/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/db.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/main.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/main.py new file mode 100644 index 0000000..89b6584 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/main.py @@ -0,0 +1,4 @@ +from models import create_tables, drop_tables + +create_tables() +# drop_tables() \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/models.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/models.py new file mode 100644 index 0000000..5d5fcd7 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/models.py @@ -0,0 +1,37 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model/User Table +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" + +# Address Model +class Address(Base): + __tablename__ = "address" + + id: Mapped[int] = mapped_column(primary_key=True) + street: Mapped[str] = mapped_column(String(50), nullable=False) + dist: Mapped[str] = mapped_column(String, nullable=False, unique=True) + country: Mapped[str] = mapped_column(String, nullable=False, unique=True) + def __repr__(self) -> str: + return f"Address(id={self.id!r}, street={self.street!r})" + +# Create Table +def create_tables(): + Base.metadata.create_all(engine) + +# Drop Table +def drop_tables(): + Base.metadata.drop_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/requirements.txt b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/sqlite.db b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/sqlite.db new file mode 100644 index 0000000..3d4d459 Binary files /dev/null and b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/2.Create Model/ch43/sqlite.db differ diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/README.md b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/README.md new file mode 100644 index 0000000..8567111 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy ORM Doc](https://docs.sqlalchemy.org/en/20/orm/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/db.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/main.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/main.py new file mode 100644 index 0000000..89b6584 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/main.py @@ -0,0 +1,4 @@ +from models import create_tables, drop_tables + +create_tables() +# drop_tables() \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/models.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/models.py new file mode 100644 index 0000000..54e1acd --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/models.py @@ -0,0 +1,81 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship +from sqlalchemy import String, ForeignKey, Table, Column, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + # One-to-Many: User to Post + posts: Mapped[list["Post"]] = relationship("Post", back_populates="user", cascade="all, delete") + + # One-to-One: User to Profile + profile: Mapped["Profile"] = relationship("Profile", back_populates="user", uselist=False, cascade="all, delete") + + # Many-to-Many: User to Address + address: Mapped[list["Address"]] = relationship("Address", back_populates="user", cascade="all, delete") + + def __repr__(self) -> str: + return f"" + +# Post Model One-to-Many +class Post(Base): + __tablename__ = "posts" + + id: Mapped[int] = mapped_column(primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False) + title: Mapped[str] = mapped_column(String, nullable=False) + content: Mapped[str] = mapped_column(String, nullable=False) + + user: Mapped["User"] = relationship("User", back_populates="posts") + + def __repr__(self) -> str: + return f"" + +# Profile Model (One-to-One) +class Profile(Base): + __tablename__ = "profile" + + id: Mapped[int] = mapped_column(primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True) + bio: Mapped[str] = mapped_column(String, nullable=False) + + user: Mapped["User"] = relationship("User", back_populates="profile") + + def __repr__(self) -> str: + return f"" + +# Address Model (Many-to-Many) +class Address(Base): + __tablename__ = "address" + + id: Mapped[int] = mapped_column(primary_key=True) + street: Mapped[str] = mapped_column(String, nullable=False) + country: Mapped[str] = mapped_column(String, nullable=False) + + user: Mapped[list["User"]] = relationship("User", back_populates="address") + + def __repr__(self) -> str: + return f"
" + +user_address_association = Table( + "user_address_association", + Base.metadata, + Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), + Column("address_id", Integer, ForeignKey("address.id", ondelete="CASCADE"), primary_key=True), +) + +# Create Table +def create_tables(): + Base.metadata.create_all(engine) + +# Drop Table +def drop_tables(): + Base.metadata.drop_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/requirements.txt b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/sqlite.db b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/sqlite.db new file mode 100644 index 0000000..32e2019 Binary files /dev/null and b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/3.Relationship/ch44/sqlite.db differ diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/README.md b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/README.md new file mode 100644 index 0000000..8567111 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/README.md @@ -0,0 +1,7 @@ +- [SQLAlchemy ORM Doc](https://docs.sqlalchemy.org/en/20/orm/index.html) + +- Install SQLAlchemy using pip: + + ```bash + pip install SQLAlchemy + ``` \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/db.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/main.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/main.py new file mode 100644 index 0000000..0854fcb --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/main.py @@ -0,0 +1,30 @@ +from models import create_tables, drop_tables +from services import * + +# create_tables() +# drop_tables() + +# Create Data +# result = create_user("sonam", "sonam@example.com") +# print(result) +# create_user("raj", "raj@example.com") +# create_post(1, "Hello World", "This is Sonam's first post") +# create_post(2, "Raj's Post", "Hi from Raj!") +# create_post(2, "Raj's Post New", "Hi from Raj! New") + + +# Read data +# print(get_user_by_id(1)) +# print(get_post_by_id(1)) +# print(get_all_users()) +# print(get_posts_by_user(2)) + + +# Update data +# update_user_email(1, "sonam@newdomain.com") + +# Delete Data +# delete_post(3) + +# OrderBY +# print(get_users_ordered_by_name()) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/models.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/models.py new file mode 100644 index 0000000..f47d7e3 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/models.py @@ -0,0 +1,42 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship +from sqlalchemy import String, ForeignKey +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + # One-to-Many: User to Post + posts: Mapped[list["Post"]] = relationship("Post", back_populates="user", cascade="all, delete") + + def __repr__(self) -> str: + return f"" + +# Post Model One-to-Many +class Post(Base): + __tablename__ = "posts" + + id: Mapped[int] = mapped_column(primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False) + title: Mapped[str] = mapped_column(String, nullable=False) + content: Mapped[str] = mapped_column(String, nullable=False) + + user: Mapped["User"] = relationship("User", back_populates="posts") + + def __repr__(self) -> str: + return f"" + +# Create Table +def create_tables(): + Base.metadata.create_all(engine) + +# Drop Table +def drop_tables(): + Base.metadata.drop_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/requirements.txt b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/requirements.txt new file mode 100644 index 0000000..2fd4bb1 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/requirements.txt @@ -0,0 +1,3 @@ +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/services.py b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/services.py new file mode 100644 index 0000000..ed8f001 --- /dev/null +++ b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/services.py @@ -0,0 +1,70 @@ +from models import User, Post +from db import SessionLocal +from sqlalchemy import select, asc + +# Insert or Create user +def create_user(name: str, email:str): + with SessionLocal() as session: + user = User(name=name, email=email) + session.add(user) + session.commit() + session.refresh(user) + return user + +# Insert or Create Post +def create_post(user_id: int, title: str, content:str): + with SessionLocal() as session: + post = Post(user_id=user_id, title=title, content=content) + session.add(post) + session.commit() + +# Read user by ID +def get_user_by_id(user_id: int): + with SessionLocal() as session: + user = session.get_one(User, user_id) + return user + +# Read post by ID +def get_post_by_id(post_id: int): + with SessionLocal() as session: + stmt = select(Post).where(Post.id == post_id) + post = session.scalars(stmt).one() + return post + +# Read All user +def get_all_users(): + with SessionLocal() as session: + stmt = select(User) + users = session.scalars(stmt).all() + return users + +# Read all posts for an user +def get_posts_by_user(user_id: int): + with SessionLocal() as session: + user = session.get(User, user_id) + posts = user.posts if user else [] + return posts + +# Update user email +def update_user_email(user_id: int, new_email: str): + with SessionLocal() as session: + user = session.get(User, user_id) + if user: + user.email = new_email + session.commit() + return user + +# Delete Post +def delete_post(post_id: int): + with SessionLocal() as session: + post = session.get(Post, post_id) + if post: + session.delete(post) + session.commit() + +# Order by +def get_users_ordered_by_name(): + with SessionLocal() as session: + stmt = select(User).order_by(asc(User.name)) + users = session.scalars(stmt).all() + return users \ No newline at end of file diff --git a/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/sqlite.db b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/sqlite.db new file mode 100644 index 0000000..2f98553 Binary files /dev/null and b/Geekyshows/Section-11.Working with Databases Using SQLAlchemy ORM/4.CRUD/ch45/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/README.md new file mode 100644 index 0000000..976c859 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/README.md @@ -0,0 +1,13 @@ +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/env.py new file mode 100644 index 0000000..8363b51 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/main.py new file mode 100644 index 0000000..4ab75d1 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/main.py @@ -0,0 +1,4 @@ +from models import create_tables, drop_tables + +create_tables() +# drop_tables() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/models.py new file mode 100644 index 0000000..d7da0f6 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/models.py @@ -0,0 +1,26 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + + def __repr__(self) -> str: + return f"" + +# Create Table +def create_tables(): + Base.metadata.create_all(engine) + +# Drop Table +def drop_tables(): + Base.metadata.drop_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/1.Install and Config Alembic/ch46/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/README.md new file mode 100644 index 0000000..20956a2 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/README.md @@ -0,0 +1,19 @@ +- [Alembic Create Table Doc](https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.create_table) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/env.py new file mode 100644 index 0000000..8363b51 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/versions/f100673dccd6_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/versions/f100673dccd6_create_users_table.py new file mode 100644 index 0000000..1d77248 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/alembic/versions/f100673dccd6_create_users_table.py @@ -0,0 +1,30 @@ +"""create users table + +Revision ID: f100673dccd6 +Revises: +Create Date: 2025-04-29 11:01:13.100563 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f100673dccd6' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "users", + sa.Column("id", sa.INTEGER, primary_key=True), + sa.Column("name", sa.String(50), nullable=False), + sa.Column("email", sa.String, nullable=False), +) + +def downgrade() -> None: + op.drop_table('users') diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/models.py new file mode 100644 index 0000000..7d791ee --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/models.py @@ -0,0 +1,17 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/sqlite.db new file mode 100644 index 0000000..fddf1bf Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/2.Create Database Table using Alembic Migrations/ch47/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/README.md new file mode 100644 index 0000000..5d919cc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/README.md @@ -0,0 +1,19 @@ +- [Alembic Add Column Doc](https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.add_column) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/env.py new file mode 100644 index 0000000..8363b51 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/2c1e867f03bb_add_phone_column.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/2c1e867f03bb_add_phone_column.py new file mode 100644 index 0000000..e858124 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/2c1e867f03bb_add_phone_column.py @@ -0,0 +1,26 @@ +"""add phone column + +Revision ID: 2c1e867f03bb +Revises: f100673dccd6 +Create Date: 2025-04-29 11:14:46.859490 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '2c1e867f03bb' +down_revision: Union[str, None] = 'f100673dccd6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column("users", sa.Column("phone", sa.String())) + + +def downgrade() -> None: + op.drop_column("users", "phone") diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/f100673dccd6_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/f100673dccd6_create_users_table.py new file mode 100644 index 0000000..1d77248 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/alembic/versions/f100673dccd6_create_users_table.py @@ -0,0 +1,30 @@ +"""create users table + +Revision ID: f100673dccd6 +Revises: +Create Date: 2025-04-29 11:01:13.100563 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f100673dccd6' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "users", + sa.Column("id", sa.INTEGER, primary_key=True), + sa.Column("name", sa.String(50), nullable=False), + sa.Column("email", sa.String, nullable=False), +) + +def downgrade() -> None: + op.drop_table('users') diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/models.py new file mode 100644 index 0000000..f9ea03d --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/models.py @@ -0,0 +1,18 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(Integer) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/sqlite.db new file mode 100644 index 0000000..4e9d2be Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/3.Alembic Upgrade/ch48/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/README.md new file mode 100644 index 0000000..b7acc96 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/README.md @@ -0,0 +1,19 @@ +- [Alembic Drop Column Doc](https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.drop_column) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/env.py new file mode 100644 index 0000000..8363b51 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/2c1e867f03bb_add_phone_column.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/2c1e867f03bb_add_phone_column.py new file mode 100644 index 0000000..ca0afb0 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/2c1e867f03bb_add_phone_column.py @@ -0,0 +1,26 @@ +"""add phone column + +Revision ID: 2c1e867f03bb +Revises: f100673dccd6 +Create Date: 2025-04-29 11:14:46.859490 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '2c1e867f03bb' +down_revision: Union[str, None] = 'f100673dccd6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column("users", sa.Column("phone", sa.Integer())) + + +def downgrade() -> None: + op.drop_column("users", "phone") diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/f100673dccd6_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/f100673dccd6_create_users_table.py new file mode 100644 index 0000000..1d77248 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/alembic/versions/f100673dccd6_create_users_table.py @@ -0,0 +1,30 @@ +"""create users table + +Revision ID: f100673dccd6 +Revises: +Create Date: 2025-04-29 11:01:13.100563 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f100673dccd6' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "users", + sa.Column("id", sa.INTEGER, primary_key=True), + sa.Column("name", sa.String(50), nullable=False), + sa.Column("email", sa.String, nullable=False), +) + +def downgrade() -> None: + op.drop_table('users') diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/models.py new file mode 100644 index 0000000..f9ea03d --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/models.py @@ -0,0 +1,18 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(Integer) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/sqlite.db new file mode 100644 index 0000000..53ee627 Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/4.Alembic Downgrade/ch49/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/README.md new file mode 100644 index 0000000..aa16c2b --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/README.md @@ -0,0 +1,19 @@ +- [Alembic Create Unique Constraint Doc](https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.create_unique_constraint) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/env.py new file mode 100644 index 0000000..af6dd83 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/env.py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/2c1e867f03bb_add_phone_column.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/2c1e867f03bb_add_phone_column.py new file mode 100644 index 0000000..ca0afb0 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/2c1e867f03bb_add_phone_column.py @@ -0,0 +1,26 @@ +"""add phone column + +Revision ID: 2c1e867f03bb +Revises: f100673dccd6 +Create Date: 2025-04-29 11:14:46.859490 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '2c1e867f03bb' +down_revision: Union[str, None] = 'f100673dccd6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column("users", sa.Column("phone", sa.Integer())) + + +def downgrade() -> None: + op.drop_column("users", "phone") diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/6ea47089b8f7_phone_column_unique.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/6ea47089b8f7_phone_column_unique.py new file mode 100644 index 0000000..ef603c8 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/6ea47089b8f7_phone_column_unique.py @@ -0,0 +1,28 @@ +"""phone column unique + +Revision ID: 6ea47089b8f7 +Revises: 2c1e867f03bb +Create Date: 2025-04-29 11:29:30.625995 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '6ea47089b8f7' +down_revision: Union[str, None] = '2c1e867f03bb' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + with op.batch_alter_table("users") as batch_op: + batch_op.create_unique_constraint("uq_users_phone", ["phone"]) + + +def downgrade() -> None: + with op.batch_alter_table("users") as batch_op: + batch_op.drop_constraint("uq_users_phone", type_='unique') diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/f100673dccd6_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/f100673dccd6_create_users_table.py new file mode 100644 index 0000000..1d77248 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/alembic/versions/f100673dccd6_create_users_table.py @@ -0,0 +1,30 @@ +"""create users table + +Revision ID: f100673dccd6 +Revises: +Create Date: 2025-04-29 11:01:13.100563 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f100673dccd6' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "users", + sa.Column("id", sa.INTEGER, primary_key=True), + sa.Column("name", sa.String(50), nullable=False), + sa.Column("email", sa.String, nullable=False), +) + +def downgrade() -> None: + op.drop_table('users') diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/models.py new file mode 100644 index 0000000..f39087c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/models.py @@ -0,0 +1,18 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(Integer, unique=True) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/sqlite.db new file mode 100644 index 0000000..a3157e7 Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/5.Adding Constraints/ch50/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/README.md new file mode 100644 index 0000000..78fab4e --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/README.md @@ -0,0 +1,19 @@ +- [Alembic Operation Reference Doc](https://alembic.sqlalchemy.org/en/latest/ops.html) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/env.py new file mode 100644 index 0000000..af6dd83 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/env.py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/df80115fb104_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/df80115fb104_create_users_table.py new file mode 100644 index 0000000..ca9360c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/df80115fb104_create_users_table.py @@ -0,0 +1,40 @@ +"""create users table + +Revision ID: df80115fb104 +Revises: +Create Date: 2025-04-29 11:47:55.252574 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'df80115fb104' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.Column('phone', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint('phone') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/e9fe3f51099f_add_address_column.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/e9fe3f51099f_add_address_column.py new file mode 100644 index 0000000..3a09001 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/alembic/versions/e9fe3f51099f_add_address_column.py @@ -0,0 +1,36 @@ +"""add address column + +Revision ID: e9fe3f51099f +Revises: df80115fb104 +Create Date: 2025-04-29 11:50:27.009395 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e9fe3f51099f' +down_revision: Union[str, None] = 'df80115fb104' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.add_column(sa.Column('address', sa.String(), nullable=False)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.drop_column('address') + + # ### end Alembic commands ### diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/models.py new file mode 100644 index 0000000..0ae8856 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/models.py @@ -0,0 +1,19 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(Integer, unique=True) + address: Mapped[str] = mapped_column(String, nullable=False) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/sqlite.db new file mode 100644 index 0000000..daee015 Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/6.Autogenerate Alembic Migrations/ch51/sqlite.db differ diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/README.md b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/README.md new file mode 100644 index 0000000..5e9c7c7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/README.md @@ -0,0 +1,19 @@ +- [Alembic Commands Doc](https://alembic.sqlalchemy.org/en/latest/api/commands.html) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Apply the migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic.ini b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/README b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/env.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/env.py new file mode 100644 index 0000000..af6dd83 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/env.py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/script.py.mako b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/df80115fb104_create_users_table.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/df80115fb104_create_users_table.py new file mode 100644 index 0000000..ca9360c --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/df80115fb104_create_users_table.py @@ -0,0 +1,40 @@ +"""create users table + +Revision ID: df80115fb104 +Revises: +Create Date: 2025-04-29 11:47:55.252574 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'df80115fb104' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.Column('phone', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint('phone') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/e9fe3f51099f_add_address_column.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/e9fe3f51099f_add_address_column.py new file mode 100644 index 0000000..3a09001 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/alembic/versions/e9fe3f51099f_add_address_column.py @@ -0,0 +1,36 @@ +"""add address column + +Revision ID: e9fe3f51099f +Revises: df80115fb104 +Create Date: 2025-04-29 11:50:27.009395 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e9fe3f51099f' +down_revision: Union[str, None] = 'df80115fb104' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.add_column(sa.Column('address', sa.String(), nullable=False)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.drop_column('address') + + # ### end Alembic commands ### diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/commandsnote.md b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/commandsnote.md new file mode 100644 index 0000000..51cc4bc --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/commandsnote.md @@ -0,0 +1,63 @@ +- Initialize a new Alembic environment in the specified directory. This sets up the configuration files and script directory needed for migrations. + + ```bash + alembic init [directory] + ``` + +- Create a new migration script. + + - Use -m to add a message describing the migration. + + - Add --autogenerate to automatically generate migration code based on model and database differences. + + ```bash + alembic revision -m "message" [--autogenerate] + ``` + +- Apply migrations up to the specified revision (e.g., head for the latest). This updates the database schema to the desired state. + + ```bash + alembic upgrade [revision] + ``` + +- This command upgrades your database schema forward by two migration steps from the current revision, applying the next two migrations in sequence + + ```bash + alembic upgrade +2 + ``` + +- Revert migrations down to the specified revision, effectively rolling back schema changes. + + ```bash + alembic downgrade [revision] + ``` + +- This command downgrades (rolls back) your database schema by one migration step from the current revision, reverting the most recent migration + + ```bash + alembic downgrade -1 + ``` + +- Display the current revision(s) applied to the database. + + ```bash + alembic current + ``` + +- Show the list of all migration scripts in chronological order. + + ```bash + alembic history + ``` + +- Check if there are pending upgrade operations when using autogenerate (available in newer versions). + + ```bash + alembic check + ``` + +- List available migration environment templates for initializing new projects. + + ```bash + alembic list_templates + ``` diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/db.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/db.py new file mode 100644 index 0000000..e3b4085 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/main.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/main.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/main.py @@ -0,0 +1 @@ + diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/models.py b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/models.py new file mode 100644 index 0000000..0ae8856 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/models.py @@ -0,0 +1,19 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String, Integer +from db import engine + +class Base(DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + phone: Mapped[int] = mapped_column(Integer, unique=True) + address: Mapped[str] = mapped_column(String, nullable=False) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/requirements.txt b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/requirements.txt new file mode 100644 index 0000000..dd20de7 --- /dev/null +++ b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/requirements.txt @@ -0,0 +1,6 @@ +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 \ No newline at end of file diff --git a/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/sqlite.db b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/sqlite.db new file mode 100644 index 0000000..daee015 Binary files /dev/null and b/Geekyshows/Section-12.Alembic Manage Database migrations/7.Alembic History/ch52/sqlite.db differ diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/README.md b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/README.md new file mode 100644 index 0000000..805a7d1 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/README.md @@ -0,0 +1,13 @@ +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/db.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/db.py new file mode 100644 index 0000000..1e03727 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/db.py @@ -0,0 +1,6 @@ +from sqlalchemy.ext.asyncio import create_async_engine + +DATABASE_URL = "sqlite+aiosqlite:///./sqlite.db" + +engine = create_async_engine(DATABASE_URL, echo=True) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/main.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/main.py new file mode 100644 index 0000000..d9d946b --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/main.py @@ -0,0 +1,23 @@ +from tables import create_tables, drop_tables +import asyncio +from services import * + +async def main(): + # Create Tables + await create_tables() + + # Create Data + # await create_user("sonam", "sonam@example.com") + # await create_user("raj", "raj@example.com") + + # Read data + # print(await get_user_by_id(1)) + # print(await get_all_users()) + + # Update data + # await update_user_email(1, "sonam@newdomain.com") + + # Delete Data + # await delete_user(2) + +asyncio.run(main()) \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/requirements.txt b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/requirements.txt new file mode 100644 index 0000000..7877741 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/requirements.txt @@ -0,0 +1,4 @@ +aiosqlite==0.21.0 +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/services.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/services.py new file mode 100644 index 0000000..382989d --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/services.py @@ -0,0 +1,38 @@ +from db import engine +from tables import users +from sqlalchemy import insert, select, update, delete + +# Insert or Create User +async def create_user(name: str, email: str): + async with engine.connect() as conn: + stmt = insert(users).values(name=name, email=email) + await conn.execute(stmt) + await conn.commit() + +# Get Single User by ID +async def get_user_by_id(user_id: int): + async with engine.connect() as conn: + stmt = select(users).where(users.c.id == user_id) + result = await conn.execute(stmt) + return result.first() + +# Get All Users +async def get_all_users(): + async with engine.connect() as conn: + stmt = select(users) + result = await conn.execute(stmt) + return result.fetchall() + +# Update User Email +async def update_user_email(user_id: int, new_email: str): + async with engine.connect() as conn: + stmt = update(users).where(users.c.id == user_id).values(email=new_email) + await conn.execute(stmt) + await conn.commit() + +# Delete User +async def delete_user(user_id: int): + async with engine.connect() as conn: + stmt = delete(users).where(users.c.id == user_id) + await conn.execute(stmt) + await conn.commit() diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/sqlite.db b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/sqlite.db new file mode 100644 index 0000000..4473d5c Binary files /dev/null and b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/sqlite.db differ diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/tables.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/tables.py new file mode 100644 index 0000000..b9652e6 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/1.Async SQLAlchemy Core/ch53/tables.py @@ -0,0 +1,21 @@ +from sqlalchemy import MetaData, Table, Column, Integer, String +from db import engine + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) + +async def create_tables(): + async with engine.begin() as conn: + await conn.run_sync(metadata.create_all) + +async def drop_tables(): + async with engine.begin() as conn: + await conn.run_sync(metadata.drop_all) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/README.md b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/README.md new file mode 100644 index 0000000..5437b34 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/README.md @@ -0,0 +1,29 @@ +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- [Using Asyncio with Alembic Doc](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic.ini b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic.ini new file mode 100644 index 0000000..0e933f3 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///./sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/README b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/env.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/env.py new file mode 100644 index 0000000..1154bf3 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from tables import metadata + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/script.py.mako b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/versions/d8486b281e3f_create_users_table.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/versions/d8486b281e3f_create_users_table.py new file mode 100644 index 0000000..4b00590 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/alembic/versions/d8486b281e3f_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: d8486b281e3f +Revises: +Create Date: 2025-04-30 14:20:50.901704 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'd8486b281e3f' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/db.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/db.py new file mode 100644 index 0000000..1e03727 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/db.py @@ -0,0 +1,6 @@ +from sqlalchemy.ext.asyncio import create_async_engine + +DATABASE_URL = "sqlite+aiosqlite:///./sqlite.db" + +engine = create_async_engine(DATABASE_URL, echo=True) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/main.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/main.py new file mode 100644 index 0000000..4f01050 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/main.py @@ -0,0 +1,19 @@ +import asyncio +from services import * + +async def main(): + # Create Data + await create_user("sonam", "sonam@example.com") + # await create_user("raj", "raj@example.com") + + # Read data + # print(await get_user_by_id(1)) + # print(await get_all_users()) + + # Update data + # await update_user_email(1, "sonam@newdomain.com") + + # Delete Data + # await delete_user(2) + +asyncio.run(main()) \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/requirements.txt b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/requirements.txt new file mode 100644 index 0000000..ad2c065 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/requirements.txt @@ -0,0 +1,7 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/services.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/services.py new file mode 100644 index 0000000..382989d --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/services.py @@ -0,0 +1,38 @@ +from db import engine +from tables import users +from sqlalchemy import insert, select, update, delete + +# Insert or Create User +async def create_user(name: str, email: str): + async with engine.connect() as conn: + stmt = insert(users).values(name=name, email=email) + await conn.execute(stmt) + await conn.commit() + +# Get Single User by ID +async def get_user_by_id(user_id: int): + async with engine.connect() as conn: + stmt = select(users).where(users.c.id == user_id) + result = await conn.execute(stmt) + return result.first() + +# Get All Users +async def get_all_users(): + async with engine.connect() as conn: + stmt = select(users) + result = await conn.execute(stmt) + return result.fetchall() + +# Update User Email +async def update_user_email(user_id: int, new_email: str): + async with engine.connect() as conn: + stmt = update(users).where(users.c.id == user_id).values(email=new_email) + await conn.execute(stmt) + await conn.commit() + +# Delete User +async def delete_user(user_id: int): + async with engine.connect() as conn: + stmt = delete(users).where(users.c.id == user_id) + await conn.execute(stmt) + await conn.commit() diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/sqlite.db b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/sqlite.db new file mode 100644 index 0000000..52e9eba Binary files /dev/null and b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/sqlite.db differ diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/tables.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/tables.py new file mode 100644 index 0000000..88198df --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/2.Async SQLAlchemy Core with Alembic/ch54/tables.py @@ -0,0 +1,11 @@ +from sqlalchemy import MetaData, Table, Column, Integer, String + +metadata = MetaData() + +users = Table( + "users", + metadata, + Column("id", Integer, primary_key=True), + Column("name", String(length=50), nullable=False), + Column("email", String, nullable=False, unique=True) + ) diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/README.md b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/README.md new file mode 100644 index 0000000..805a7d1 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/README.md @@ -0,0 +1,13 @@ +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/db.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/db.py new file mode 100644 index 0000000..33dd38c --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/db.py @@ -0,0 +1,8 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker + +DATABASE_URL = "sqlite+aiosqlite:///sqlite.db" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/main.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/main.py new file mode 100644 index 0000000..1ca72b7 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/main.py @@ -0,0 +1,23 @@ +from models import create_tables, drop_tables +import asyncio +from services import * + +async def main(): + # Create Tables + # await create_tables() + + # Create Data + # await create_user("sonam", "sonam@example.com") + # await create_user("raj", "raj@example.com") + + # Read data + # print(await get_user_by_id(1)) + # print(await get_all_users()) + + # Update data + # await update_user_email(1, "sonam@newdomain.com") + + # Delete Data + await delete_user(2) + +asyncio.run(main()) \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/models.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/models.py new file mode 100644 index 0000000..c16596c --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/models.py @@ -0,0 +1,27 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String +from sqlalchemy.ext.asyncio import AsyncAttrs +from db import engine + +class Base(AsyncAttrs, DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" + +async def create_tables(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + +async def drop_tables(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.drop_all) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/requirements.txt b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/requirements.txt new file mode 100644 index 0000000..7877741 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/requirements.txt @@ -0,0 +1,4 @@ +aiosqlite==0.21.0 +greenlet==3.2.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/services.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/services.py new file mode 100644 index 0000000..d0a4f7f --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/services.py @@ -0,0 +1,40 @@ +from models import User +from db import async_session +from sqlalchemy import select + +# Insert or Create user +async def create_user(name: str, email: str): + async with async_session() as session: + user = User(name=name, email=email) + session.add(user) + await session.commit() + +# Read user by ID +async def get_user_by_id(user_id: int): + async with async_session() as session: + user = await session.get(User, user_id) + return user + +# Read All user +async def get_all_users(): + async with async_session() as session: + stmt = select(User) + users = await session.scalars(stmt) + return users.all() + +# Update user email +async def update_user_email(user_id: int, new_email: str): + async with async_session() as session: + user = await session.get(User, user_id) + if user: + user.email = new_email + await session.commit() + return user + +# Delete User +async def delete_user(user_id: int): + async with async_session() as session: + user = await session.get(User, user_id) + if user: + await session.delete(user) + await session.commit() \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/sqlite.db b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/sqlite.db new file mode 100644 index 0000000..4473d5c Binary files /dev/null and b/Geekyshows/Section-13.SQLAlchemy Asynchronous/3.Async SQLAlchemy ORM/ch55/sqlite.db differ diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/README.md b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/README.md new file mode 100644 index 0000000..5437b34 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/README.md @@ -0,0 +1,29 @@ +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- [Using Asyncio with Alembic Doc](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic.ini b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/README b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/env.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/env.py new file mode 100644 index 0000000..53c30d6 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/script.py.mako b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/versions/ddbb469923ff_create_users_table.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/versions/ddbb469923ff_create_users_table.py new file mode 100644 index 0000000..882f032 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/alembic/versions/ddbb469923ff_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: ddbb469923ff +Revises: +Create Date: 2025-04-30 14:50:11.692343 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'ddbb469923ff' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/db.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/db.py new file mode 100644 index 0000000..33dd38c --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/db.py @@ -0,0 +1,8 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker + +DATABASE_URL = "sqlite+aiosqlite:///sqlite.db" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/main.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/main.py new file mode 100644 index 0000000..1ca72b7 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/main.py @@ -0,0 +1,23 @@ +from models import create_tables, drop_tables +import asyncio +from services import * + +async def main(): + # Create Tables + # await create_tables() + + # Create Data + # await create_user("sonam", "sonam@example.com") + # await create_user("raj", "raj@example.com") + + # Read data + # print(await get_user_by_id(1)) + # print(await get_all_users()) + + # Update data + # await update_user_email(1, "sonam@newdomain.com") + + # Delete Data + await delete_user(2) + +asyncio.run(main()) \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/models.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/models.py new file mode 100644 index 0000000..c16596c --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/models.py @@ -0,0 +1,27 @@ +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy import String +from sqlalchemy.ext.asyncio import AsyncAttrs +from db import engine + +class Base(AsyncAttrs, DeclarativeBase): + pass + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" + +async def create_tables(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + +async def drop_tables(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.drop_all) + diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/requirements.txt b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/requirements.txt new file mode 100644 index 0000000..ad2c065 --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/requirements.txt @@ -0,0 +1,7 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +greenlet==3.2.2 +Mako==1.3.10 +MarkupSafe==3.0.2 +SQLAlchemy==2.0.41 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/services.py b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/services.py new file mode 100644 index 0000000..d0a4f7f --- /dev/null +++ b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/services.py @@ -0,0 +1,40 @@ +from models import User +from db import async_session +from sqlalchemy import select + +# Insert or Create user +async def create_user(name: str, email: str): + async with async_session() as session: + user = User(name=name, email=email) + session.add(user) + await session.commit() + +# Read user by ID +async def get_user_by_id(user_id: int): + async with async_session() as session: + user = await session.get(User, user_id) + return user + +# Read All user +async def get_all_users(): + async with async_session() as session: + stmt = select(User) + users = await session.scalars(stmt) + return users.all() + +# Update user email +async def update_user_email(user_id: int, new_email: str): + async with async_session() as session: + user = await session.get(User, user_id) + if user: + user.email = new_email + await session.commit() + return user + +# Delete User +async def delete_user(user_id: int): + async with async_session() as session: + user = await session.get(User, user_id) + if user: + await session.delete(user) + await session.commit() \ No newline at end of file diff --git a/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/sqlite.db b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/sqlite.db new file mode 100644 index 0000000..d11b8ec Binary files /dev/null and b/Geekyshows/Section-13.SQLAlchemy Asynchronous/4.Async SQLAlchemy ORM with Alembic/ch56/sqlite.db differ diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/README.md new file mode 100644 index 0000000..3bd701a --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/README.md @@ -0,0 +1,41 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Installation Doc](https://docs.sqlalchemy.org/en/20/intro.html#installation) + +- Install SQLAlchemy + + ```bash + pip install SQLAlchemy + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/env.py new file mode 100644 index 0000000..f3aec14 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/env.py @@ -0,0 +1,80 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/base.py new file mode 100644 index 0000000..2278416 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/base.py @@ -0,0 +1,4 @@ +from sqlalchemy.orm import DeclarativeBase + +class Base(DeclarativeBase): + pass \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/config.py new file mode 100644 index 0000000..19e0f86 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/db/config.py @@ -0,0 +1,12 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/main.py new file mode 100644 index 0000000..9bb71ec --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/app/main.py @@ -0,0 +1,3 @@ +from fastapi import FastAPI + +app = FastAPI() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/requirements.txt new file mode 100644 index 0000000..35568d1 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/1.Setup and Config FastAPI with Sync SQLAlchemy and Alembic/ch57/requirements.txt @@ -0,0 +1,39 @@ +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/README.md new file mode 100644 index 0000000..82ca2ac --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/README.md @@ -0,0 +1,53 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Installation Doc](https://docs.sqlalchemy.org/en/20/intro.html#installation) + +- Install SQLAlchemy + + ```bash + pip install SQLAlchemy + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Create migration + + ```bash + alembic revision --autogenerate -m "create users table" + ``` + +- Run migration + + ```bash + alembic upgrade head + ``` \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/env.py new file mode 100644 index 0000000..f3aec14 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/env.py @@ -0,0 +1,80 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/15800b2abf63_create_products_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/15800b2abf63_create_products_table.py new file mode 100644 index 0000000..e63e309 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/15800b2abf63_create_products_table.py @@ -0,0 +1,36 @@ +"""create products table + +Revision ID: 15800b2abf63 +Revises: c75b98518025 +Create Date: 2025-05-02 10:27:31.284191 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '15800b2abf63' +down_revision: Union[str, None] = 'c75b98518025' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('products', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('products') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/c75b98518025_create_users_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/c75b98518025_create_users_table.py new file mode 100644 index 0000000..502d5c9 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/alembic/versions/c75b98518025_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: c75b98518025 +Revises: +Create Date: 2025-05-02 10:23:10.628470 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c75b98518025' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/base.py new file mode 100644 index 0000000..942b73d --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/base.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import DeclarativeBase + +class Base(DeclarativeBase): + pass + +from app.user import models as user_models +from app.product import models as product_models \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/config.py new file mode 100644 index 0000000..19e0f86 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/db/config.py @@ -0,0 +1,12 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/main.py new file mode 100644 index 0000000..9bb71ec --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/main.py @@ -0,0 +1,3 @@ +from fastapi import FastAPI + +app = FastAPI() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/product/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/product/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/product/models.py new file mode 100644 index 0000000..a202b42 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/product/models.py @@ -0,0 +1,13 @@ +from app.db.base import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String + +# Product Model +class Product(Base): + __tablename__ = "products" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + + def __repr__(self) -> str: + return f"" diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/user/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/user/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/user/models.py new file mode 100644 index 0000000..9a9a074 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/app/user/models.py @@ -0,0 +1,14 @@ +from app.db.base import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/requirements.txt new file mode 100644 index 0000000..35568d1 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/requirements.txt @@ -0,0 +1,39 @@ +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/sqlite.db b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/sqlite.db new file mode 100644 index 0000000..1a944f1 Binary files /dev/null and b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/2.Create FastAPI Model using Sync SQLAlchemy and Alembic/ch58/sqlite.db differ diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/README.md new file mode 100644 index 0000000..074b6c5 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/README.md @@ -0,0 +1,75 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Installation Doc](https://docs.sqlalchemy.org/en/20/intro.html#installation) + +- Install SQLAlchemy + + ```bash + pip install SQLAlchemy + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- Initialize Alembic + + ```bash + alembic init alembic + ``` + +- Create migration + + ```bash + alembic revision --autogenerate -m "create users table" + ``` + +- Run migration + + ```bash + alembic upgrade head + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic.ini new file mode 100644 index 0000000..991ce63 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic.ini @@ -0,0 +1,119 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/env.py new file mode 100644 index 0000000..f3aec14 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/env.py @@ -0,0 +1,80 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata, + render_as_batch=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/15800b2abf63_create_products_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/15800b2abf63_create_products_table.py new file mode 100644 index 0000000..e63e309 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/15800b2abf63_create_products_table.py @@ -0,0 +1,36 @@ +"""create products table + +Revision ID: 15800b2abf63 +Revises: c75b98518025 +Create Date: 2025-05-02 10:27:31.284191 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '15800b2abf63' +down_revision: Union[str, None] = 'c75b98518025' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('products', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('products') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/c75b98518025_create_users_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/c75b98518025_create_users_table.py new file mode 100644 index 0000000..502d5c9 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/alembic/versions/c75b98518025_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: c75b98518025 +Revises: +Create Date: 2025-05-02 10:23:10.628470 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c75b98518025' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/base.py new file mode 100644 index 0000000..942b73d --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/base.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import DeclarativeBase + +class Base(DeclarativeBase): + pass + +from app.user import models as user_models +from app.product import models as product_models \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/config.py new file mode 100644 index 0000000..19e0f86 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/db/config.py @@ -0,0 +1,12 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +SessionLocal = sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/main.py new file mode 100644 index 0000000..caa1a80 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/main.py @@ -0,0 +1,19 @@ +from fastapi import FastAPI +from app.user import services as user_services +from pydantic import BaseModel + +app = FastAPI() + +class UserCreate(BaseModel): + name: str + email: str + +@app.post("/user") +def user_create(user: UserCreate): + user_services.create_user(name=user.name, email=user.email) + return {"status": "created"} + +@app.get("/users") +def all_users(): + users = user_services.get_all_users() + return users \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/product/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/product/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/product/models.py new file mode 100644 index 0000000..a202b42 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/product/models.py @@ -0,0 +1,13 @@ +from app.db.base import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String + +# Product Model +class Product(Base): + __tablename__ = "products" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + + def __repr__(self) -> str: + return f"" diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/models.py new file mode 100644 index 0000000..9a9a074 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/models.py @@ -0,0 +1,14 @@ +from app.db.base import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/services.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/services.py new file mode 100644 index 0000000..3696eb4 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/app/user/services.py @@ -0,0 +1,17 @@ +from app.db.config import SessionLocal +from app.user.models import User +from sqlalchemy import select + +# Insert or Create User +def create_user(name: str, email: str): + with SessionLocal() as session: + user = User(name=name, email=email) + session.add(user) + session.commit() + +# Read All User +def get_all_users(): + with SessionLocal() as session: + stmt = select(User) + users = session.scalars(stmt) + return users.all() \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/requirements.txt new file mode 100644 index 0000000..35568d1 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/requirements.txt @@ -0,0 +1,39 @@ +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/sqlite.db b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/sqlite.db new file mode 100644 index 0000000..adb97db Binary files /dev/null and b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/3.Insert Fetch Data using FastAPI and Sync SQLAlchemy/ch59/sqlite.db differ diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/README.md new file mode 100644 index 0000000..1eb29fd --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/README.md @@ -0,0 +1,49 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- [Using Asyncio with Alembic Doc](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic) + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/base.py new file mode 100644 index 0000000..9a1e770 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/base.py @@ -0,0 +1,5 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/config.py new file mode 100644 index 0000000..bf59449 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/db/config.py @@ -0,0 +1,11 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/main.py new file mode 100644 index 0000000..b21386e --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/app/main.py @@ -0,0 +1,4 @@ +from fastapi import FastAPI + +app = FastAPI() + diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/4.Setup and Config FastAPI with Async SQLAlchemy and Alembic/ch60/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/README.md new file mode 100644 index 0000000..acec0cc --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/README.md @@ -0,0 +1,61 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- [Using Asyncio with Alembic Doc](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic) + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` + +- Create migration + + ```bash + alembic revision --autogenerate -m "create users table" + ``` + +- Run migration + + ```bash + alembic upgrade head + ``` diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/4625f7e72652_create_users_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/4625f7e72652_create_users_table.py new file mode 100644 index 0000000..2716087 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/4625f7e72652_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: 4625f7e72652 +Revises: +Create Date: 2025-05-02 11:54:31.436980 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '4625f7e72652' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/92df89f0057d_create_products_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/92df89f0057d_create_products_table.py new file mode 100644 index 0000000..e3543d3 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/alembic/versions/92df89f0057d_create_products_table.py @@ -0,0 +1,36 @@ +"""create products table + +Revision ID: 92df89f0057d +Revises: 4625f7e72652 +Create Date: 2025-05-02 11:56:40.904319 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '92df89f0057d' +down_revision: Union[str, None] = '4625f7e72652' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('products', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('products') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/base.py new file mode 100644 index 0000000..6fde1f4 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/base.py @@ -0,0 +1,8 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass + +from app.user import models as user_models +from app.product import models as product_models \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/config.py new file mode 100644 index 0000000..bf59449 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/db/config.py @@ -0,0 +1,11 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/main.py new file mode 100644 index 0000000..b21386e --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/main.py @@ -0,0 +1,4 @@ +from fastapi import FastAPI + +app = FastAPI() + diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/product/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/product/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/product/models.py new file mode 100644 index 0000000..ada2fa1 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/product/models.py @@ -0,0 +1,13 @@ +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String +from app.db.base import Base + +# Product Model +class Product(Base): + __tablename__ = "products" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/user/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/user/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/user/models.py new file mode 100644 index 0000000..6625426 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/app/user/models.py @@ -0,0 +1,14 @@ +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String +from app.db.base import Base + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/sqlite.db b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/sqlite.db new file mode 100644 index 0000000..0691b2a Binary files /dev/null and b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/5.Create FastAPI Model using Async SQLAlchemy and Alembic/ch61/sqlite.db differ diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/README.md b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/README.md new file mode 100644 index 0000000..a270ca3 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/README.md @@ -0,0 +1,83 @@ +- [FastAPI Installation Doc](https://fastapi.tiangolo.com/#installation) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- [SQLAlchemy Asynchronous I/O (asyncio) Doc](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) + +- Install SQLAlchemy with async support + + ```bash + pip install sqlalchemy[asyncio] + ``` + +- Install aiosqlite for SQLite + + ```bash + pip install aiosqlite + ``` + +- [Alembic’s Installation Doc](https://alembic.sqlalchemy.org/en/latest/front.html#installation) + +- Install Alembic + + ```bash + pip install alembic + ``` + +- [Using Asyncio with Alembic Doc](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic) + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` + +- Create migration + + ```bash + alembic revision --autogenerate -m "create users table" + ``` + +- Run migration + + ```bash + alembic upgrade head + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic.ini b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/README b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/env.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/script.py.mako b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/4625f7e72652_create_users_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/4625f7e72652_create_users_table.py new file mode 100644 index 0000000..2716087 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/4625f7e72652_create_users_table.py @@ -0,0 +1,38 @@ +"""create users table + +Revision ID: 4625f7e72652 +Revises: +Create Date: 2025-05-02 11:54:31.436980 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '4625f7e72652' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/92df89f0057d_create_products_table.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/92df89f0057d_create_products_table.py new file mode 100644 index 0000000..e3543d3 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/alembic/versions/92df89f0057d_create_products_table.py @@ -0,0 +1,36 @@ +"""create products table + +Revision ID: 92df89f0057d +Revises: 4625f7e72652 +Create Date: 2025-05-02 11:56:40.904319 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '92df89f0057d' +down_revision: Union[str, None] = '4625f7e72652' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('products', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('products') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/base.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/base.py new file mode 100644 index 0000000..6fde1f4 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/base.py @@ -0,0 +1,8 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass + +from app.user import models as user_models +from app.product import models as product_models \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/config.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/config.py new file mode 100644 index 0000000..bf59449 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/db/config.py @@ -0,0 +1,11 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +import os +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/main.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/main.py new file mode 100644 index 0000000..9a6f5e2 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/main.py @@ -0,0 +1,21 @@ +from fastapi import FastAPI +from app.user import services as user_services +from pydantic import BaseModel + +app = FastAPI() + +class UserCreate(BaseModel): + name: str + email: str + +@app.post("/user") +async def user_create(user: UserCreate): + await user_services.create_user(name=user.name, email=user.email) + return {"status":"created"} + +@app.get("/users") +async def all_users(): + users = await user_services.get_all_users() + return users + + diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/product/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/product/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/product/models.py new file mode 100644 index 0000000..ada2fa1 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/product/models.py @@ -0,0 +1,13 @@ +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String +from app.db.base import Base + +# Product Model +class Product(Base): + __tablename__ = "products" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/__init__.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/models.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/models.py new file mode 100644 index 0000000..6625426 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/models.py @@ -0,0 +1,14 @@ +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String +from app.db.base import Base + +# User Model +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(50), nullable=False) + email: Mapped[str] = mapped_column(String, nullable=False, unique=True) + + def __repr__(self) -> str: + return f"" \ No newline at end of file diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/services.py b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/services.py new file mode 100644 index 0000000..2dfe20b --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/app/user/services.py @@ -0,0 +1,15 @@ +from app.db.config import async_session +from app.user.models import User +from sqlalchemy import select + +async def create_user(name: str, email: str): + async with async_session() as session: + user = User(name=name, email=email) + session.add(user) + await session.commit() + +async def get_all_users(): + async with async_session() as session: + stmt = select(User) + users = await session.scalars(stmt) + return users.all() diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/requirements.txt b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/sqlite.db b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/sqlite.db new file mode 100644 index 0000000..cb44c9c Binary files /dev/null and b/Geekyshows/Section-14.Fast API with SQLAlchemy and Alembic/6.Insert Fetch Data using FastAPI and Async SQLAlchemy/ch62/sqlite.db differ diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/README.md b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/README.md new file mode 100644 index 0000000..aa4f23e --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/README.md @@ -0,0 +1,57 @@ +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" "sqlalchemy[asyncio]" alembic aiosqlite + ``` + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` + +- Generate create note table migration + + ```bash + alembic revision --autogenerate -m "create note table" + ``` + +- Apply migration + + ```bash + alembic upgrade head + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic.ini b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/README b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/env.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/script.py.mako b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/versions/39d141844545_create_notes_table.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/versions/39d141844545_create_notes_table.py new file mode 100644 index 0000000..60a764b --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/alembic/versions/39d141844545_create_notes_table.py @@ -0,0 +1,37 @@ +"""create notes table + +Revision ID: 39d141844545 +Revises: +Create Date: 2025-05-03 10:37:29.229227 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '39d141844545' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('notes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('content', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('notes') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/base.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/base.py new file mode 100644 index 0000000..c9832ed --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/base.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass + +from app.notes import models as notes_models \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/config.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/config.py new file mode 100644 index 0000000..30a920c --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/db/config.py @@ -0,0 +1,12 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/main.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/main.py new file mode 100644 index 0000000..ecc2e4f --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/main.py @@ -0,0 +1,37 @@ +from fastapi import FastAPI +from app.notes import services as notes_services +app = FastAPI() + +@app.post("/notes") +async def note_create(new_note: dict): + note = await notes_services.create_note(new_note["title"], new_note["content"]) + return note + +@app.get("/notes/{note_id}") +async def note_get(note_id: int): + note = await notes_services.get_note(note_id) + return note + +@app.get("/notes") +async def note_get_all(): + notes = await notes_services.get_all_notes() + return notes + +@app.put("/notes/{note_id}") +async def note_update(note_id: int, new_note: dict): + new_title = new_note.get("title") + new_content = new_note.get("content") + note = await notes_services.update_note(note_id, new_title, new_content) + return note + +@app.patch("/notes/{note_id}") +async def note_patch(note_id: int, new_note: dict): + new_title = new_note.get("title") + new_content = new_note.get("content") + note = await notes_services.patch_note(note_id, new_title, new_content) + return note + +@app.delete("/notes/{note_id}") +async def note_delete(note_id: int): + response = await notes_services.delete_note(note_id) + return response \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/models.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/models.py new file mode 100644 index 0000000..7a5d4f1 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/models.py @@ -0,0 +1,10 @@ +from sqlalchemy.orm import Mapped, mapped_column +from app.db.base import Base +from sqlalchemy import String, Text + +class Note(Base): + __tablename__ = "notes" + + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] = mapped_column(String) + content: Mapped[str] = mapped_column(Text) \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/services.py b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/services.py new file mode 100644 index 0000000..a7487a9 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/app/notes/services.py @@ -0,0 +1,60 @@ +from app.db.config import async_session +from app.notes.models import Note +from sqlalchemy import select +from fastapi import HTTPException + +async def create_note(title: str, content: str): + async with async_session() as session: + note = Note(title=title, content=content) + session.add(note) + await session.commit() + await session.refresh(note) + return note + +async def get_note(note_id: int): + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + return note + +async def get_all_notes(): + async with async_session() as session: + stmt = select(Note) + notes = await session.scalars(stmt) + return notes.all() + +async def update_note(note_id: int, new_title: str, new_content: str): + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + note.title = new_title + note.content = new_content + await session.commit() + await session.refresh(note) + return note + +async def patch_note(note_id: int, new_title: str | None = None, new_content: str | None = None): + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + + if new_title is not None: + note.title = new_title + if new_content is not None: + note.content = new_content + + await session.commit() + await session.refresh(note) + return note + +async def delete_note(note_id: int): + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + await session.delete(note) + await session.commit() + return {"message": "deleted"} \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/requirements.txt b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/sqlite.db b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/sqlite.db new file mode 100644 index 0000000..41270be Binary files /dev/null and b/Geekyshows/Section-15.Project 1 Fastnotes/1.Fastnotes V1/fastnotes_v1/sqlite.db differ diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/README.md b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/README.md new file mode 100644 index 0000000..aa4f23e --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/README.md @@ -0,0 +1,57 @@ +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" "sqlalchemy[asyncio]" alembic aiosqlite + ``` + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` + +- Generate create note table migration + + ```bash + alembic revision --autogenerate -m "create note table" + ``` + +- Apply migration + + ```bash + alembic upgrade head + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic.ini b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/README b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/env.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/script.py.mako b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/versions/39d141844545_create_notes_table.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/versions/39d141844545_create_notes_table.py new file mode 100644 index 0000000..60a764b --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/alembic/versions/39d141844545_create_notes_table.py @@ -0,0 +1,37 @@ +"""create notes table + +Revision ID: 39d141844545 +Revises: +Create Date: 2025-05-03 10:37:29.229227 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '39d141844545' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('notes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('content', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('notes') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/base.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/base.py new file mode 100644 index 0000000..c9832ed --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/base.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass + +from app.notes import models as notes_models \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/config.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/config.py new file mode 100644 index 0000000..30a920c --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/db/config.py @@ -0,0 +1,12 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/main.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/main.py new file mode 100644 index 0000000..a51a70e --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/main.py @@ -0,0 +1,35 @@ +from fastapi import FastAPI +from app.notes import services as notes_services +from app.notes.schemas import NoteCreate, NoteUpdate, NotePatch, NoteOut +from typing import List +app = FastAPI() + +@app.post("/notes", response_model=NoteOut) +async def note_create(new_note: NoteCreate): + note = await notes_services.create_note(new_note) + return note + +@app.get("/notes/{note_id}", response_model=NoteOut) +async def note_get(note_id: int): + note = await notes_services.get_note(note_id) + return note + +@app.get("/notes", response_model=List[NoteOut]) +async def note_get_all(): + notes = await notes_services.get_all_notes() + return notes + +@app.put("/notes/{note_id}", response_model=NoteOut) +async def note_update(note_id: int, new_note: NoteUpdate): + note = await notes_services.update_note(note_id, new_note) + return note + +@app.patch("/notes/{note_id}", response_model=NoteOut) +async def note_patch(note_id: int, new_note: NotePatch): + note = await notes_services.patch_note(note_id, new_note) + return note + +@app.delete("/notes/{note_id}") +async def note_delete(note_id: int): + response = await notes_services.delete_note(note_id) + return response \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/models.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/models.py new file mode 100644 index 0000000..7a5d4f1 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/models.py @@ -0,0 +1,10 @@ +from sqlalchemy.orm import Mapped, mapped_column +from app.db.base import Base +from sqlalchemy import String, Text + +class Note(Base): + __tablename__ = "notes" + + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] = mapped_column(String) + content: Mapped[str] = mapped_column(Text) \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/schemas.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/schemas.py new file mode 100644 index 0000000..6c87a43 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/schemas.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel, ConfigDict + +# Shared base fields +class NoteBase(BaseModel): + title: str + content: str + +# For creation +class NoteCreate(NoteBase): + pass + +# For full update (PUT) +class NoteUpdate(NoteBase): + pass + +# For partial update (PATCH) +class NotePatch(BaseModel): + title: str | None = None + content: str | None = None + +# For response serialization +class NoteOut(NoteBase): + id: int + model_config = ConfigDict(from_attributes=True) diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/services.py b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/services.py new file mode 100644 index 0000000..ad20feb --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/app/notes/services.py @@ -0,0 +1,61 @@ +from app.db.config import async_session +from app.notes.models import Note +from sqlalchemy import select +from fastapi import HTTPException +from app.notes.schemas import NoteCreate, NoteUpdate, NotePatch, NoteOut + +async def create_note(new_note: NoteCreate) -> NoteOut: + async with async_session() as session: + note = Note(title=new_note.title, content=new_note.content) + session.add(note) + await session.commit() + await session.refresh(note) + return note + +async def get_note(note_id: int) -> NoteOut: + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + return note + +async def get_all_notes() -> list[NoteOut]: + async with async_session() as session: + stmt = select(Note) + notes = await session.scalars(stmt) + return notes.all() + +async def update_note(note_id: int, new_note: NoteUpdate) -> NoteOut: + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + note.title = new_note.title + note.content = new_note.content + await session.commit() + await session.refresh(note) + return note + +async def patch_note(note_id: int, new_note: NotePatch) -> NoteOut: + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + + if new_note.title is not None: + note.title = new_note.title + if new_note.content is not None: + note.content = new_note.content + + await session.commit() + await session.refresh(note) + return note + +async def delete_note(note_id: int): + async with async_session() as session: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + await session.delete(note) + await session.commit() + return {"message": "deleted"} \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/requirements.txt b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/sqlite.db b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/sqlite.db new file mode 100644 index 0000000..e3a6b62 Binary files /dev/null and b/Geekyshows/Section-15.Project 1 Fastnotes/2.Fastnotes V2/fastnotes_v2/sqlite.db differ diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/README.md b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/README.md new file mode 100644 index 0000000..5e0dd87 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/README.md @@ -0,0 +1,59 @@ +- [FastAPI Dependencies Doc](https://fastapi.tiangolo.com/tutorial/dependencies/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" "sqlalchemy[asyncio]" alembic aiosqlite + ``` + +- Initialize Alembic + + ```bash + alembic init -t async alembic + ``` + +- Generate create note table migration + + ```bash + alembic revision --autogenerate -m "create note table" + ``` + +- Apply migration + + ```bash + alembic upgrade head + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic.ini b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic.ini new file mode 100644 index 0000000..b8c398f --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic.ini @@ -0,0 +1,117 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite+aiosqlite:///sqlite.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/README b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/env.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/env.py new file mode 100644 index 0000000..31c75ee --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from app.db.base import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata, render_as_batch=True) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/script.py.mako b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/versions/39d141844545_create_notes_table.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/versions/39d141844545_create_notes_table.py new file mode 100644 index 0000000..60a764b --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/alembic/versions/39d141844545_create_notes_table.py @@ -0,0 +1,37 @@ +"""create notes table + +Revision ID: 39d141844545 +Revises: +Create Date: 2025-05-03 10:37:29.229227 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '39d141844545' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('notes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('content', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('notes') + # ### end Alembic commands ### diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/base.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/base.py new file mode 100644 index 0000000..c9832ed --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/base.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.ext.asyncio import AsyncAttrs + +class Base(AsyncAttrs, DeclarativeBase): + pass + +from app.notes import models as notes_models \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/config.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/config.py new file mode 100644 index 0000000..2007a0b --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/db/config.py @@ -0,0 +1,35 @@ +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession +import os +from fastapi import Depends +from typing import Annotated + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" + +engine = create_async_engine(DATABASE_URL, echo=True) + +async_session = async_sessionmaker(bind=engine, expire_on_commit=False) + +# async def get_db(): +# async with async_session() as session: +# yield session + +async def get_db(): + session = async_session() + print(f"Creating new AsyncSession: {id(session)}") + try: + async with session: + yield session + print(f"Session committed and closed: {id(session)}") + except Exception as e: + print(f"Error in session: {id(session)}, rolling back: {str(e)}") + await session.rollback() + raise + finally: + await session.close() + print(f"Session explicitly closed: {id(session)}") + +SessionDep = Annotated[AsyncSession, Depends(get_db)] diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/main.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/main.py new file mode 100644 index 0000000..01c7bee --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/main.py @@ -0,0 +1,40 @@ +from fastapi import FastAPI +from app.notes import services as notes_services +from app.notes.schemas import NoteCreate, NoteUpdate, NotePatch, NoteOut +from typing import List +from app.db.config import SessionDep + +app = FastAPI() + +# @app.post("/notes", response_model=NoteOut) +# async def note_create(new_note: NoteCreate, session: AsyncSession = Depends(get_db)): + +@app.post("/notes", response_model=NoteOut) +async def note_create(session: SessionDep, new_note: NoteCreate): + note = await notes_services.create_note(session, new_note) + return note + +@app.get("/notes/{note_id}", response_model=NoteOut) +async def note_get(session: SessionDep, note_id: int): + note = await notes_services.get_note(session, note_id) + return note + +@app.get("/notes", response_model=List[NoteOut]) +async def note_get_all(session: SessionDep): + notes = await notes_services.get_all_notes(session) + return notes + +@app.put("/notes/{note_id}", response_model=NoteOut) +async def note_update(session: SessionDep, note_id: int, new_note: NoteUpdate): + note = await notes_services.update_note(session, note_id, new_note) + return note + +@app.patch("/notes/{note_id}", response_model=NoteOut) +async def note_patch(session: SessionDep, note_id: int, new_note: NotePatch): + note = await notes_services.patch_note(session, note_id, new_note) + return note + +@app.delete("/notes/{note_id}") +async def note_delete(session: SessionDep, note_id: int): + response = await notes_services.delete_note(session, note_id) + return response \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/__init__.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/models.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/models.py new file mode 100644 index 0000000..7a5d4f1 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/models.py @@ -0,0 +1,10 @@ +from sqlalchemy.orm import Mapped, mapped_column +from app.db.base import Base +from sqlalchemy import String, Text + +class Note(Base): + __tablename__ = "notes" + + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] = mapped_column(String) + content: Mapped[str] = mapped_column(Text) \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/schemas.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/schemas.py new file mode 100644 index 0000000..6c87a43 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/schemas.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel, ConfigDict + +# Shared base fields +class NoteBase(BaseModel): + title: str + content: str + +# For creation +class NoteCreate(NoteBase): + pass + +# For full update (PUT) +class NoteUpdate(NoteBase): + pass + +# For partial update (PATCH) +class NotePatch(BaseModel): + title: str | None = None + content: str | None = None + +# For response serialization +class NoteOut(NoteBase): + id: int + model_config = ConfigDict(from_attributes=True) diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/services.py b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/services.py new file mode 100644 index 0000000..f412b19 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/app/notes/services.py @@ -0,0 +1,58 @@ +from app.db.config import async_session +from app.notes.models import Note +from sqlalchemy import select +from fastapi import HTTPException +from app.notes.schemas import NoteCreate, NoteUpdate, NotePatch, NoteOut +from sqlalchemy.ext.asyncio import AsyncSession + +# async def create_note(new_note: NoteCreate, session: AsyncSession)-> NoteOut: + +async def create_note(session: AsyncSession, new_note: NoteCreate) -> NoteOut: + note = Note(title=new_note.title, content=new_note.content) + session.add(note) + await session.commit() + await session.refresh(note) + return note + +async def get_note(session: AsyncSession, note_id: int) -> NoteOut: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + return note + +async def get_all_notes(session: AsyncSession) -> list[NoteOut]: + stmt = select(Note) + notes = await session.scalars(stmt) + return notes.all() + +async def update_note(session: AsyncSession, note_id: int, new_note: NoteUpdate) -> NoteOut: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + note.title = new_note.title + note.content = new_note.content + await session.commit() + await session.refresh(note) + return note + +async def patch_note(session: AsyncSession, note_id: int, new_note: NotePatch) -> NoteOut: + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + + if new_note.title is not None: + note.title = new_note.title + if new_note.content is not None: + note.content = new_note.content + + await session.commit() + await session.refresh(note) + return note + +async def delete_note(session: AsyncSession, note_id: int): + note = await session.get(Note, note_id) + if note is None: + raise HTTPException(status_code=404, detail="Not not found") + await session.delete(note) + await session.commit() + return {"message": "deleted"} \ No newline at end of file diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/requirements.txt b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/requirements.txt new file mode 100644 index 0000000..f0d4038 --- /dev/null +++ b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/requirements.txt @@ -0,0 +1,40 @@ +aiosqlite==0.21.0 +alembic==1.15.2 +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/sqlite.db b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/sqlite.db new file mode 100644 index 0000000..a853681 Binary files /dev/null and b/Geekyshows/Section-15.Project 1 Fastnotes/3.Fastnotes V3/fastnotes_v3/sqlite.db differ diff --git a/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/README.md b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/README.md new file mode 100644 index 0000000..ed7ae9f --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/README.md @@ -0,0 +1,7 @@ +- [SQLModel Doc](https://sqlmodel.tiangolo.com/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/db.py b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/db.py new file mode 100644 index 0000000..981391b --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/db.py @@ -0,0 +1,5 @@ +from sqlmodel import create_engine + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/requirements.txt b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/requirements.txt new file mode 100644 index 0000000..d363e82 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/1.Install SQL Model and Create Engine/ch63/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/README.md b/Geekyshows/Section-16.SQL Model/2.Create Model/README.md new file mode 100644 index 0000000..a444b16 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/2.Create Model/README.md @@ -0,0 +1,7 @@ +- [Create a Table with SQLModel - Use the Engine Doc](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#create-the-table-model-class) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/db.py b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/db.py new file mode 100644 index 0000000..c6e3363 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/db.py @@ -0,0 +1,8 @@ +from sqlmodel import create_engine, SQLModel + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/main.py b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/main.py new file mode 100644 index 0000000..ec5dea5 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/main.py @@ -0,0 +1,3 @@ +from db import create_tables +from models import User +create_tables() \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/models.py b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/models.py new file mode 100644 index 0000000..5a29ae6 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/models.py @@ -0,0 +1,6 @@ +from sqlmodel import Field, SQLModel + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/requirements.txt b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/requirements.txt new file mode 100644 index 0000000..b6d6fab --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.1 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.40 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/sqlite.db b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/sqlite.db new file mode 100644 index 0000000..5232320 Binary files /dev/null and b/Geekyshows/Section-16.SQL Model/2.Create Model/ch64/sqlite.db differ diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/README.md b/Geekyshows/Section-16.SQL Model/3.Database Operations/README.md new file mode 100644 index 0000000..100a2ca --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/README.md @@ -0,0 +1,13 @@ +- [SQLModel Create Rows - Use the Session - INSERT Doc](https://sqlmodel.tiangolo.com/tutorial/insert/) + +- [SQLModel Read Data - SELECT Doc](https://sqlmodel.tiangolo.com/tutorial/select/) + +- [SQLModel Filter Data - WHERE Doc](https://sqlmodel.tiangolo.com/tutorial/where/) + +- [SQLModel Read One Row Doc](https://sqlmodel.tiangolo.com/tutorial/one/#read-the-first-row) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/db.py b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/db.py new file mode 100644 index 0000000..c6e3363 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/db.py @@ -0,0 +1,8 @@ +from sqlmodel import create_engine, SQLModel + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/main.py b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/main.py new file mode 100644 index 0000000..66b8ca3 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/main.py @@ -0,0 +1,13 @@ +from db import create_tables +from services import * + +# create_tables() + +# new_user = create_user("sonam", "sonam@example.com") +# print(new_user) + +# print(get_all_users()) + +# print(get_user(1)) + +# print(get_user_with_get(1)) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/models.py b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/models.py new file mode 100644 index 0000000..5a29ae6 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/models.py @@ -0,0 +1,6 @@ +from sqlmodel import Field, SQLModel + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/requirements.txt b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/requirements.txt new file mode 100644 index 0000000..d363e82 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/services.py b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/services.py new file mode 100644 index 0000000..9179c20 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/services.py @@ -0,0 +1,28 @@ +from sqlmodel import Session, select +from db import engine +from models import User + +def create_user(name: str, email:str): + with Session(engine) as session: + user = User(name=name, email=email) + session.add(user) + session.commit() + session.refresh(user) + return user + +def get_all_users(): + with Session(engine) as session: + stmt = select(User) + users = session.exec(stmt) + return users.all() + +def get_user(user_id: int): + with Session(engine) as session: + stmt = select(User).where(User.id == user_id) + user = session.exec(stmt).first() + return user + +def get_user_with_get(user_id: int): + with Session(engine) as session: + user = session.get(User, user_id) + return user \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/sqlite.db b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/sqlite.db new file mode 100644 index 0000000..b7b2d84 Binary files /dev/null and b/Geekyshows/Section-16.SQL Model/3.Database Operations/ch65/sqlite.db differ diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/README.md b/Geekyshows/Section-16.SQL Model/4.Relationship/README.md new file mode 100644 index 0000000..f99d910 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/4.Relationship/README.md @@ -0,0 +1,7 @@ +- [SQLModel Define Relationships Attributes Doc](https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/define-relationships-attributes/#declare-relationship-attributes) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/db.py b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/db.py new file mode 100644 index 0000000..c6e3363 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/db.py @@ -0,0 +1,8 @@ +from sqlmodel import create_engine, SQLModel + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/main.py b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/main.py new file mode 100644 index 0000000..a778ecf --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/main.py @@ -0,0 +1,5 @@ +from db import create_tables +from models import User, Profile, Address, UserAddressLink, Post + +create_tables() + diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/models.py b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/models.py new file mode 100644 index 0000000..a3102a0 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/models.py @@ -0,0 +1,29 @@ +from sqlmodel import Field, SQLModel + +class UserAddressLink(SQLModel, table=True): + user_id : int = Field(foreign_key="user.id", primary_key=True) + address_id : int = Field(foreign_key="address.id", primary_key=True) + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str + +# One to One +class Profile(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id", unique=True) + bio: str + +# One to Many +class Post(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id") + title: str + content: str + +# Many to Many +class Address(SQLModel, table=True): + id: int = Field(primary_key=True) + street: str + city: str \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/requirements.txt b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/requirements.txt new file mode 100644 index 0000000..d363e82 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/sqlite.db b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/sqlite.db new file mode 100644 index 0000000..9035296 Binary files /dev/null and b/Geekyshows/Section-16.SQL Model/4.Relationship/ch66/sqlite.db differ diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/README.md b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/README.md new file mode 100644 index 0000000..64299ab --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/README.md @@ -0,0 +1,7 @@ +- [SQLModel Relationship Attributes - Intro Doc](https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/db.py b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/db.py new file mode 100644 index 0000000..c6e3363 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/db.py @@ -0,0 +1,8 @@ +from sqlmodel import create_engine, SQLModel + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/main.py b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/main.py new file mode 100644 index 0000000..a778ecf --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/main.py @@ -0,0 +1,5 @@ +from db import create_tables +from models import User, Profile, Address, UserAddressLink, Post + +create_tables() + diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/models.py b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/models.py new file mode 100644 index 0000000..cd8a15c --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/models.py @@ -0,0 +1,48 @@ +from sqlmodel import Field, SQLModel, Relationship + +class UserAddressLink(SQLModel, table=True): + user_id : int = Field(foreign_key="user.id", primary_key=True) + address_id : int = Field(foreign_key="address.id", primary_key=True) + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str + + profile : "Profile" | None = Relationship(back_populates="user") + posts : list["Post"] = Relationship(back_populates="user") + address: list["Address"] = Relationship(back_populates="user", link_model=UserAddressLink) + +# One to One +class Profile(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id", unique=True) + bio: str + + user : "User" = Relationship(back_populates="profile") + +# user.profile.bio +# profile.user.name + +# One to Many +class Post(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id") + title: str + content: str + + user: "User" = Relationship(back_populates="posts") + +# user.posts +# post.user + +# Many to Many +class Address(SQLModel, table=True): + id: int = Field(primary_key=True) + street: str + city: str + + user: list["User"] = Relationship(back_populates="address", link_model=UserAddressLink) + + # user.address + # address.user \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/requirements.txt b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/requirements.txt new file mode 100644 index 0000000..d363e82 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/sqlite.db b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/sqlite.db new file mode 100644 index 0000000..9035296 Binary files /dev/null and b/Geekyshows/Section-16.SQL Model/5.Relationship Attributes/ch67/sqlite.db differ diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/README.md b/Geekyshows/Section-16.SQL Model/6.Cascade/README.md new file mode 100644 index 0000000..1171f9a --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/6.Cascade/README.md @@ -0,0 +1,7 @@ +- [SQLModel Cascade Delete Relationships Doc](https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/cascade-delete-relationships/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/db.py b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/db.py new file mode 100644 index 0000000..c6e3363 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/db.py @@ -0,0 +1,8 @@ +from sqlmodel import create_engine, SQLModel + +DATABASE_URL = "sqlite:///sqlite.db" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/main.py b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/main.py new file mode 100644 index 0000000..a778ecf --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/main.py @@ -0,0 +1,5 @@ +from db import create_tables +from models import User, Profile, Address, UserAddressLink, Post + +create_tables() + diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/models.py b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/models.py new file mode 100644 index 0000000..6ea7de8 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/models.py @@ -0,0 +1,39 @@ +from sqlmodel import Field, SQLModel, Relationship + +class UserAddressLink(SQLModel, table=True): + user_id : int = Field(foreign_key="user.id", primary_key=True, ondelete="CASCADE") + address_id : int = Field(foreign_key="address.id", primary_key=True, ondelete="CASCADE") + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str + + profile : "Profile" = Relationship(back_populates="user", cascade_delete=True) + posts : list["Post"] = Relationship(back_populates="user", cascade_delete=True) + address: list["Address"] = Relationship(back_populates="user", link_model=UserAddressLink, cascade_delete=True) + +# One to One +class Profile(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id", unique=True, ondelete="CASCADE") + bio: str + + user : "User" = Relationship(back_populates="profile") + +# One to Many +class Post(SQLModel, table=True): + id: int = Field(primary_key=True) + user_id : int = Field(foreign_key="user.id", ondelete="SET NULL", nullable=True) + title: str + content: str + + user: "User" = Relationship(back_populates="posts") + +# Many to Many +class Address(SQLModel, table=True): + id: int = Field(primary_key=True) + street: str + city: str + + user: list["User"] = Relationship(back_populates="address", link_model=UserAddressLink, cascade_delete=True) diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/requirements.txt b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/requirements.txt new file mode 100644 index 0000000..d363e82 --- /dev/null +++ b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/requirements.txt @@ -0,0 +1,8 @@ +annotated-types==0.7.0 +greenlet==3.2.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +typing-inspection==0.4.0 +typing_extensions==4.13.2 diff --git a/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/sqlite.db b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/sqlite.db new file mode 100644 index 0000000..5062739 Binary files /dev/null and b/Geekyshows/Section-16.SQL Model/6.Cascade/ch68/sqlite.db differ diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/README.md b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/README.md new file mode 100644 index 0000000..134843d --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/README.md @@ -0,0 +1,7 @@ +- [FastAPI SQLModel Doc](https://fastapi.tiangolo.com/tutorial/sql-databases/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/db/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/db/config.py b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/db/config.py new file mode 100644 index 0000000..5b36c0b --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/db/config.py @@ -0,0 +1,15 @@ +from sqlmodel import SQLModel, create_engine +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/main.py b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/main.py new file mode 100644 index 0000000..ba3e247 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/app/main.py @@ -0,0 +1,3 @@ +from fastapi import FastAPI + +app = FastAPI() \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/requirements.txt b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/requirements.txt new file mode 100644 index 0000000..3cdfd72 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/1.Config SQL Model with FastAPI/ch69/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/README.md b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/README.md new file mode 100644 index 0000000..134843d --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/README.md @@ -0,0 +1,7 @@ +- [FastAPI SQLModel Doc](https://fastapi.tiangolo.com/tutorial/sql-databases/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/db/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/db/config.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/db/config.py new file mode 100644 index 0000000..5b36c0b --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/db/config.py @@ -0,0 +1,15 @@ +from sqlmodel import SQLModel, create_engine +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/main.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/main.py new file mode 100644 index 0000000..7f01039 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/main.py @@ -0,0 +1,12 @@ +from fastapi import FastAPI +from app.user.models import User +from app.product.models import Product +from app.db.config import create_tables +from contextlib import asynccontextmanager + +@asynccontextmanager +async def lifespan(app: FastAPI): + create_tables() + yield + +app = FastAPI(lifespan=lifespan) \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/product/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/product/models.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/product/models.py new file mode 100644 index 0000000..0b055c7 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/product/models.py @@ -0,0 +1,5 @@ +from sqlmodel import Field, SQLModel + +class Product(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/user/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/user/models.py b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/user/models.py new file mode 100644 index 0000000..92ca048 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/app/user/models.py @@ -0,0 +1,6 @@ +from sqlmodel import Field, SQLModel + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/requirements.txt b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/requirements.txt new file mode 100644 index 0000000..3cdfd72 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/sqlite.db b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/sqlite.db new file mode 100644 index 0000000..c2c41d3 Binary files /dev/null and b/Geekyshows/Section-17.FastAPI with SQL Model/2.Create Model/ch70/sqlite.db differ diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/README.md b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/README.md new file mode 100644 index 0000000..134843d --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/README.md @@ -0,0 +1,7 @@ +- [FastAPI SQLModel Doc](https://fastapi.tiangolo.com/tutorial/sql-databases/) + +- Install SQLModel + + ```bash + pip install sqlmodel + ``` diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/db/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/db/config.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/db/config.py new file mode 100644 index 0000000..5b36c0b --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/db/config.py @@ -0,0 +1,15 @@ +from sqlmodel import SQLModel, create_engine +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# print(BASE_DIR) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/main.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/main.py new file mode 100644 index 0000000..b746ef5 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/main.py @@ -0,0 +1,21 @@ +from fastapi import FastAPI +from app.db.config import create_tables +from contextlib import asynccontextmanager +from app.user.services import * + +@asynccontextmanager +async def lifespan(app: FastAPI): + create_tables() + yield + +app = FastAPI(lifespan=lifespan) + +@app.post("/user") +def user_create(new_user: dict): + user = create_user(name=new_user["name"], email=new_user["email"]) + return user + +@app.get("/users") +def all_users(): + users = get_all_users() + return users \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/product/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/product/models.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/product/models.py new file mode 100644 index 0000000..0b055c7 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/product/models.py @@ -0,0 +1,5 @@ +from sqlmodel import Field, SQLModel + +class Product(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/__init__.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/models.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/models.py new file mode 100644 index 0000000..92ca048 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/models.py @@ -0,0 +1,6 @@ +from sqlmodel import Field, SQLModel + +class User(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + email: str \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/services.py b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/services.py new file mode 100644 index 0000000..b2ee87e --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/app/user/services.py @@ -0,0 +1,17 @@ +from sqlmodel import Session, select +from app.db.config import engine +from app.user.models import User + +def create_user(name: str, email: str): + user = User(name=name, email=email) + with Session(engine) as session: + session.add(user) + session.commit() + session.refresh(user) + return user + +def get_all_users(): + with Session(engine) as session: + stmt = select(User) + users = session.exec(stmt) + return users.all() \ No newline at end of file diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/requirements.txt b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/requirements.txt new file mode 100644 index 0000000..3cdfd72 --- /dev/null +++ b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/sqlite.db b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/sqlite.db new file mode 100644 index 0000000..3386f48 Binary files /dev/null and b/Geekyshows/Section-17.FastAPI with SQL Model/3.Database Operations/ch71/sqlite.db differ diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/README.md b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/README.md new file mode 100644 index 0000000..f5618e2 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/README.md @@ -0,0 +1,39 @@ +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" sqlmodel + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/db/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/db/config.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/db/config.py new file mode 100644 index 0000000..ed5236b --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/db/config.py @@ -0,0 +1,14 @@ +from sqlmodel import SQLModel, create_engine +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/main.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/main.py new file mode 100644 index 0000000..7e366a7 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/main.py @@ -0,0 +1,41 @@ +from fastapi import FastAPI +from contextlib import asynccontextmanager +from app.db.config import create_tables +from app.task.services import * + +@asynccontextmanager +async def lifespan(app: FastAPI): + create_tables() + yield + +app = FastAPI(lifespan=lifespan) + +@app.post("/task") +def task_create(new_task: dict): + task = create_task(title=new_task["title"], content=new_task["content"]) + return task + +@app.get("/tasks") +def all_tasks(): + tasks = get_all_tasks() + return tasks + +@app.get("/task/{task_id}") +def get_task(task_id: int): + task = get_task_by_id(task_id) + return task + +@app.put("/task/{task_id}") +def put_task(task_id: int, new_task: dict): + task = update_task(task_id, title=new_task["title"], content=new_task["content"]) + return task + +@app.patch("/task/{task_id}") +def task_patch(task_id: int, new_task: dict): + task = patch_task(task_id, title=new_task.get("title"), content=new_task.get("content")) + return task + +@app.delete("/task/{task_id}") +def task_delete(task_id: int): + task = delete_task(task_id) + return task \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/models.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/models.py new file mode 100644 index 0000000..3918eb1 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/models.py @@ -0,0 +1,6 @@ +from sqlmodel import Field, SQLModel + +class Task(SQLModel, table=True): + id: int = Field(primary_key=True) + title: str + content: str \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/services.py b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/services.py new file mode 100644 index 0000000..c7d3449 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/app/task/services.py @@ -0,0 +1,60 @@ +from sqlmodel import Session, select +from app.task.models import Task +from app.db.config import engine +from fastapi import HTTPException + +def create_task(title: str, content: str): + task = Task(title=title, content=content) + with Session(engine) as session: + session.add(task) + session.commit() + session.refresh(task) + return task + +def get_all_tasks(): + with Session(engine) as session: + stmt = select(Task) + tasks = session.exec(stmt) + return tasks.all() + +def get_task_by_id(task_id: int): + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + return task + +def update_task(task_id: int, title: str, content: str): + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task.title = title + task.content = content + session.add(task) + session.commit() + session.refresh(task) + return task + +def patch_task(task_id: int, title: str | None = None, content: str | None = None): + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + if title is not None: + task.title = title + if content is not None: + task.content = content + session.add(task) + session.commit() + session.refresh(task) + return task + +def delete_task(task_id: int): + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + session.delete(task) + session.commit() + return task \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/requirements.txt b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/requirements.txt new file mode 100644 index 0000000..3cdfd72 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/sqlite.db b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/sqlite.db new file mode 100644 index 0000000..456544c Binary files /dev/null and b/Geekyshows/Section-18.Project 2 Taskkaro/1.Taskkaro V1/taskkaro_v1/sqlite.db differ diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/README.md b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/README.md new file mode 100644 index 0000000..f5618e2 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/README.md @@ -0,0 +1,39 @@ +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" sqlmodel + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/db/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/db/config.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/db/config.py new file mode 100644 index 0000000..ed5236b --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/db/config.py @@ -0,0 +1,14 @@ +from sqlmodel import SQLModel, create_engine +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/main.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/main.py new file mode 100644 index 0000000..3ef2b84 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/main.py @@ -0,0 +1,41 @@ +from fastapi import FastAPI +from contextlib import asynccontextmanager +from app.db.config import create_tables +from app.task.services import * +from app.task.models import TaskCreate, TaskUpdate, TaskPatch, TaskOut +@asynccontextmanager +async def lifespan(app: FastAPI): + create_tables() + yield + +app = FastAPI(lifespan=lifespan) + +@app.post("/task", response_model=TaskOut) +def task_create(new_task: TaskCreate): + task = create_task(new_task) + return task + +@app.get("/tasks", response_model=list[TaskOut]) +def all_tasks(): + tasks = get_all_tasks() + return tasks + +@app.get("/task/{task_id}", response_model=TaskOut) +def get_task(task_id: int): + task = get_task_by_id(task_id) + return task + +@app.put("/task/{task_id}", response_model=TaskOut) +def put_task(task_id: int, new_task: TaskUpdate): + task = update_task(task_id, new_task) + return task + +@app.patch("/task/{task_id}", response_model=TaskOut) +def task_patch(task_id: int, new_task: TaskPatch): + task = patch_task(task_id, new_task) + return task + +@app.delete("/task/{task_id}") +def task_delete(task_id: int): + task = delete_task(task_id) + return task \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/models.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/models.py new file mode 100644 index 0000000..00c0600 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/models.py @@ -0,0 +1,21 @@ +from sqlmodel import Field, SQLModel + +class TaskBase(SQLModel): + title: str + content: str + +class TaskCreate(TaskBase): + pass + +class TaskUpdate(TaskBase): + pass + +class TaskPatch(SQLModel): + title: str | None = None + content: str | None = None + +class TaskOut(TaskBase): + id: int + +class Task(TaskBase, table=True): + id: int = Field(primary_key=True) \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/services.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/services.py new file mode 100644 index 0000000..dfc1524 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/app/task/services.py @@ -0,0 +1,58 @@ +from sqlmodel import Session, select +from app.task.models import Task, TaskUpdate, TaskPatch, TaskOut +from app.db.config import engine +from fastapi import HTTPException + +def create_task(new_task: Task) -> TaskOut: + task = Task(title=new_task.title, content=new_task.content) + with Session(engine) as session: + session.add(task) + session.commit() + session.refresh(task) + return task + +def get_all_tasks() -> list[TaskOut]: + with Session(engine) as session: + stmt = select(Task) + tasks = session.exec(stmt) + return tasks.all() + +def get_task_by_id(task_id: int) -> TaskOut: + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + return task + +def update_task(task_id: int, new_task: TaskUpdate) -> TaskOut: + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task_data = new_task.model_dump() + task.sqlmodel_update(task_data) + session.add(task) + session.commit() + session.refresh(task) + return task + +def patch_task(task_id: int, new_task: TaskPatch) -> TaskOut: + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task_data = new_task.model_dump(exclude_unset=True) + task.sqlmodel_update(task_data) + session.add(task) + session.commit() + session.refresh(task) + return task + +def delete_task(task_id: int): + with Session(engine) as session: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + session.delete(task) + session.commit() + return {"ok": True} \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/requirements.txt b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/requirements.txt new file mode 100644 index 0000000..45405cc --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.1 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.4 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.40 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/sqlite.db b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/sqlite.db new file mode 100644 index 0000000..7d34a55 Binary files /dev/null and b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V2/taskkaro_v2/sqlite.db differ diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/README.md b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/README.md new file mode 100644 index 0000000..f5618e2 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/README.md @@ -0,0 +1,39 @@ +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install Dependencies + + ```bash + pip install "fastapi[standard]" sqlmodel + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/db/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/db/config.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/db/config.py new file mode 100644 index 0000000..b008e47 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/db/config.py @@ -0,0 +1,23 @@ +from sqlmodel import SQLModel, create_engine, Session +import os +from fastapi import Depends +from typing import Annotated + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +db_path= os.path.join(BASE_DIR, "sqlite.db") + +DATABASE_URL = f"sqlite:///{db_path}" + +engine = create_engine(DATABASE_URL, echo=True) + +def create_tables(): + SQLModel.metadata.create_all(engine) + +def get_session(): + with Session(engine) as session: + yield session + +SessionDep = Annotated[Session, Depends(get_session)] + + diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/main.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/main.py new file mode 100644 index 0000000..4cab91a --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/main.py @@ -0,0 +1,43 @@ +from fastapi import FastAPI +from contextlib import asynccontextmanager +from app.db.config import create_tables, SessionDep +from app.task.services import * +from app.task.models import TaskCreate, TaskUpdate, TaskPatch, TaskOut + + +@asynccontextmanager +async def lifespan(app: FastAPI): + create_tables() + yield + +app = FastAPI(lifespan=lifespan) + +@app.post("/task", response_model=TaskOut) +def task_create(session: SessionDep, new_task: TaskCreate): + task = create_task(session, new_task) + return task + +@app.get("/tasks", response_model=list[TaskOut]) +def all_tasks(session: SessionDep): + tasks = get_all_tasks(session) + return tasks + +@app.get("/task/{task_id}", response_model=TaskOut) +def get_task(session: SessionDep, task_id: int): + task = get_task_by_id(session, task_id) + return task + +@app.put("/task/{task_id}", response_model=TaskOut) +def put_task(session: SessionDep, task_id: int, new_task: TaskUpdate): + task = update_task(session, task_id, new_task) + return task + +@app.patch("/task/{task_id}", response_model=TaskOut) +def task_patch(session: SessionDep, task_id: int, new_task: TaskPatch): + task = patch_task(session, task_id, new_task) + return task + +@app.delete("/task/{task_id}") +def task_delete(session: SessionDep, task_id: int): + task = delete_task(session, task_id) + return task \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/__init__.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/models.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/models.py new file mode 100644 index 0000000..00c0600 --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/models.py @@ -0,0 +1,21 @@ +from sqlmodel import Field, SQLModel + +class TaskBase(SQLModel): + title: str + content: str + +class TaskCreate(TaskBase): + pass + +class TaskUpdate(TaskBase): + pass + +class TaskPatch(SQLModel): + title: str | None = None + content: str | None = None + +class TaskOut(TaskBase): + id: int + +class Task(TaskBase, table=True): + id: int = Field(primary_key=True) \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/services.py b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/services.py new file mode 100644 index 0000000..bed4fbd --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/app/task/services.py @@ -0,0 +1,52 @@ +from sqlmodel import Session, select +from app.task.models import Task, TaskUpdate, TaskPatch, TaskOut +from fastapi import HTTPException + +def create_task(session: Session, new_task: Task) -> TaskOut: + task = Task(title=new_task.title, content=new_task.content) + session.add(task) + session.commit() + session.refresh(task) + return task + +def get_all_tasks(session: Session) -> list[TaskOut]: + stmt = select(Task) + tasks = session.exec(stmt) + return tasks.all() + +def get_task_by_id(session: Session, task_id: int) -> TaskOut: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + return task + +def update_task(session: Session, task_id: int, new_task: TaskUpdate) -> TaskOut: + + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task_data = new_task.model_dump() + task.sqlmodel_update(task_data) + session.add(task) + session.commit() + session.refresh(task) + return task + +def patch_task(session: Session, task_id: int, new_task: TaskPatch) -> TaskOut: + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task_data = new_task.model_dump(exclude_unset=True) + task.sqlmodel_update(task_data) + session.add(task) + session.commit() + session.refresh(task) + return task + +def delete_task(session: Session, task_id: int): + task = session.get(Task, task_id) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + session.delete(task) + session.commit() + return {"ok": True} \ No newline at end of file diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/requirements.txt b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/requirements.txt new file mode 100644 index 0000000..45405cc --- /dev/null +++ b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/requirements.txt @@ -0,0 +1,38 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.1 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.4 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.40 +sqlmodel==0.0.24 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/sqlite.db b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/sqlite.db new file mode 100644 index 0000000..1429dba Binary files /dev/null and b/Geekyshows/Section-18.Project 2 Taskkaro/2.Taskkaro V3/taskkaro_v3/sqlite.db differ diff --git a/Geekyshows/Section-19.Middleware/1.Create Middleware/README.md b/Geekyshows/Section-19.Middleware/1.Create Middleware/README.md new file mode 100644 index 0000000..a73ac6d --- /dev/null +++ b/Geekyshows/Section-19.Middleware/1.Create Middleware/README.md @@ -0,0 +1,41 @@ +- [FastAPI Middleware Doc](https://fastapi.tiangolo.com/tutorial/middleware/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/app/__init__.py b/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/app/main.py b/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/app/main.py new file mode 100644 index 0000000..e4231aa --- /dev/null +++ b/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/app/main.py @@ -0,0 +1,29 @@ +from fastapi import FastAPI, Request + +app = FastAPI() + +# Creating Middleware +@app.middleware("http") +async def my_first_middleware(request: Request, call_next): + print("Middleware: Before processing the request") + print(f"Request: {request.method} {request.url}") + + response = await call_next(request) + + print("Middleware: After processing the request, before returning response") + print(f"Response status code: {response.status_code}") + + return response + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + + diff --git a/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/requirements.txt b/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/1.Create Middleware/ch72/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/README.md b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/README.md new file mode 100644 index 0000000..a73ac6d --- /dev/null +++ b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/README.md @@ -0,0 +1,41 @@ +- [FastAPI Middleware Doc](https://fastapi.tiangolo.com/tutorial/middleware/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/app/__init__.py b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/app/main.py b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/app/main.py new file mode 100644 index 0000000..a41dada --- /dev/null +++ b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/app/main.py @@ -0,0 +1,32 @@ +from fastapi import FastAPI, Request + +app = FastAPI() + +# First Middleware +@app.middleware("http") +async def my_first_middleware(request: Request, call_next): + print("1st Middleware: Before processing the request") + response = await call_next(request) + print("1st Middleware: After processing the request, before returning response") + return response + +# Second Middleware +@app.middleware("http") +async def my_second_middleware(request: Request, call_next): + print("2nd Middleware: Before processing the request") + response = await call_next(request) + print("2nd Middleware: After processing the request, before returning response") + return response + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + + diff --git a/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/requirements.txt b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/2.Multiple Middleware and Its Order/ch73/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-19.Middleware/3.Separate Middleware file/README.md b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/README.md new file mode 100644 index 0000000..a73ac6d --- /dev/null +++ b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/README.md @@ -0,0 +1,41 @@ +- [FastAPI Middleware Doc](https://fastapi.tiangolo.com/tutorial/middleware/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/__init__.py b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/main.py b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/main.py new file mode 100644 index 0000000..89b631e --- /dev/null +++ b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/main.py @@ -0,0 +1,21 @@ +from fastapi import FastAPI +from app.middlewares import my_first_middleware, my_second_middleware + +app = FastAPI() + +app.middleware("http")(my_second_middleware) +app.middleware("http")(my_first_middleware) + + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + + diff --git a/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/middlewares.py b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/middlewares.py new file mode 100644 index 0000000..de070be --- /dev/null +++ b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/app/middlewares.py @@ -0,0 +1,15 @@ +from fastapi import Request + +# First Middleware +async def my_first_middleware(request: Request, call_next): + print("1st Middleware: Before processing the request") + response = await call_next(request) + print("1st Middleware: After processing the request, before returning response") + return response + +# Second Middleware +async def my_second_middleware(request: Request, call_next): + print("2nd Middleware: Before processing the request") + response = await call_next(request) + print("2nd Middleware: After processing the request, before returning response") + return response \ No newline at end of file diff --git a/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/requirements.txt b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/3.Separate Middleware file/ch74/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/README.md b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/README.md new file mode 100644 index 0000000..a73ac6d --- /dev/null +++ b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/README.md @@ -0,0 +1,41 @@ +- [FastAPI Middleware Doc](https://fastapi.tiangolo.com/tutorial/middleware/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/__init__.py b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/main.py b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/main.py new file mode 100644 index 0000000..ba1ae57 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/main.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI +from app.middlewares import users_only_middleware, product_only_middleware, my_middleware + +app = FastAPI() + +app.middleware("http")(product_only_middleware) +app.middleware("http")(users_only_middleware) +app.middleware("http")(my_middleware) + + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + + diff --git a/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/middlewares.py b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/middlewares.py new file mode 100644 index 0000000..cb1abdf --- /dev/null +++ b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/app/middlewares.py @@ -0,0 +1,30 @@ +from fastapi import Request + +async def users_only_middleware(request: Request, call_next): + if request.url.path.startswith("/users"): + print("User Middleware: Before processing the request") + response = await call_next(request) + print("User Middleware: After processing the request, before returning response") + return response + else: + print(f"User Middleware: Skipping middleware for {request.url.path}") + response = await call_next(request) + return response + +async def product_only_middleware(request: Request, call_next): + if request.url.path.startswith("/products"): + print("Product Middleware: Before processing the request") + response = await call_next(request) + print("Product Middleware: After processing the request, before returning response") + return response + else: + print(f"Product Middleware: Skipping middleware for {request.url.path}") + response = await call_next(request) + return response + +async def my_middleware(request: Request, call_next): + print("My Middleware: Before processing the request") + response = await call_next(request) + print("My Middleware: After processing the request, before returning response") + return response + diff --git a/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/requirements.txt b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/4.Path Specific Middleware/ch75/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-19.Middleware/5.Built-in Middleware/README.md b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/README.md new file mode 100644 index 0000000..31f379d --- /dev/null +++ b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/README.md @@ -0,0 +1,57 @@ +- [FastAPI Advanced Middleware Doc](https://fastapi.tiangolo.com/advanced/middleware/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/app/__init__.py b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/app/main.py b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/app/main.py new file mode 100644 index 0000000..2bdebb7 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/app/main.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI +from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware +from fastapi.middleware.trustedhost import TrustedHostMiddleware + +app = FastAPI() + +app.add_middleware(HTTPSRedirectMiddleware) +# app.add_middleware(TrustedHostMiddleware, allowed_hosts=["localhost", "127.0.0.1"]) + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + +# curl -i http://127.0.0.1:8000/users +# curl -H "Host: localhost" http://127.0.0.1:8000/users diff --git a/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/requirements.txt b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/5.Built-in Middleware/ch76/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-19.Middleware/6.ASGI Middleware/README.md b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/README.md new file mode 100644 index 0000000..08eb163 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/README.md @@ -0,0 +1,61 @@ +- [FastAPI Advanced Middleware Doc](https://fastapi.tiangolo.com/advanced/middleware/) + +- ASGI Middlewares are Custom or third-party middlewares that follow the ASGI spec. +- ASGI middlewares are typically classes that take an ASGI app and configuration parameters. + + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/__init__.py b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/main.py b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/main.py new file mode 100644 index 0000000..3245987 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/main.py @@ -0,0 +1,19 @@ +from fastapi import FastAPI +from app.middlewares import CustomLoggingMiddleware + +app = FastAPI() + +app.add_middleware(CustomLoggingMiddleware, prefix="CUSTOM_LOG") + +@app.get("/users") +async def get_users(): + print("Endpoint: Inside get_users endpoint") + return {"data": "All Users Data"} + +@app.get("/products") +async def get_products(): + print("Endpoint: Inside get_products endpoint") + return {"data": "All products data"} + + + diff --git a/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/middlewares.py b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/middlewares.py new file mode 100644 index 0000000..e1d96c2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/app/middlewares.py @@ -0,0 +1,12 @@ +class CustomLoggingMiddleware: + def __init__(self, app, prefix="LOG"): + self.app = app + self.prefix = prefix + + async def __call__(self, scope, receive, send): + print(f"{self.prefix}: Before processing request (scope: {scope['type']})") + await self.app(scope, receive, send) + print(f"{self.prefix}: After processing request") + + + \ No newline at end of file diff --git a/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/requirements.txt b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/requirements.txt new file mode 100644 index 0000000..b8068a2 --- /dev/null +++ b/Geekyshows/Section-19.Middleware/6.ASGI Middleware/ch77/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/README.md b/Geekyshows/Section-20.API Router/1.Creating API Router/README.md new file mode 100644 index 0000000..5fac9a6 --- /dev/null +++ b/Geekyshows/Section-20.API Router/1.Creating API Router/README.md @@ -0,0 +1,57 @@ +- [FastAPI API Router Doc](https://fastapi.tiangolo.com/reference/apirouter/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/__init__.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/main.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/main.py new file mode 100644 index 0000000..3f269ff --- /dev/null +++ b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/main.py @@ -0,0 +1,33 @@ +from fastapi import FastAPI +from app.user.routers import router as user_routers +from app.product.routers import router as product_routers + +app = FastAPI() + +app.include_router(user_routers) +app.include_router(product_routers) + +@app.get("/") +async def root(): + return {"data": "Root"} + + +# @app.get("/users") +# async def get_all_users(): +# return {"data": "All Users"} + +# @app.get("/users/me") +# async def get_current_user(): +# return {"data": "Current User"} + +# @app.get("/users/{user_id}") +# async def get_single_user(user_id: int): +# return {"data": "Single User"} + +# @app.get("/products") +# async def get_all_products(): +# return {"data": "All Products"} + +# @app.get("/products/{product_id}") +# async def get_single_product(product_id: int): +# return {"data": "Single Product"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/product/__init__.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/product/routers.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/product/routers.py new file mode 100644 index 0000000..5af410f --- /dev/null +++ b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/product/routers.py @@ -0,0 +1,11 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/products") +async def get_all_products(): + return {"data": "All Products"} + +@router.get("/products/{product_id}") +async def get_single_product(product_id: int): + return {"data": "Single Product"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/user/__init__.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/user/routers.py b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/user/routers.py new file mode 100644 index 0000000..6be193c --- /dev/null +++ b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/app/user/routers.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/users") +async def get_all_users(): + return {"data": "All Users"} + +@router.get("/users/me") +async def get_current_user(): + return {"data": "Current User"} + +@router.get("/users/{user_id}") +async def get_single_user(user_id: int): + return {"data": "Single User"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/requirements.txt b/Geekyshows/Section-20.API Router/1.Creating API Router/ch78/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/2.Tags/README.md b/Geekyshows/Section-20.API Router/2.Tags/README.md new file mode 100644 index 0000000..5fac9a6 --- /dev/null +++ b/Geekyshows/Section-20.API Router/2.Tags/README.md @@ -0,0 +1,57 @@ +- [FastAPI API Router Doc](https://fastapi.tiangolo.com/reference/apirouter/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/__init__.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/main.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/main.py new file mode 100644 index 0000000..2d1f0ae --- /dev/null +++ b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/main.py @@ -0,0 +1,15 @@ +from fastapi import FastAPI +from app.user.routers import router as user_routers +from app.product.routers import router as product_routers + +app = FastAPI() + +# app.include_router(user_routers, tags=["users"]) +# app.include_router(product_routers, tags=["products"]) + +app.include_router(user_routers) +app.include_router(product_routers) + +@app.get("/") +async def root(): + return {"data": "Root"} diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/product/__init__.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/product/routers.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/product/routers.py new file mode 100644 index 0000000..5af410f --- /dev/null +++ b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/product/routers.py @@ -0,0 +1,11 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/products") +async def get_all_products(): + return {"data": "All Products"} + +@router.get("/products/{product_id}") +async def get_single_product(product_id: int): + return {"data": "Single Product"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/user/__init__.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/app/user/routers.py b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/user/routers.py new file mode 100644 index 0000000..f0e8bb5 --- /dev/null +++ b/Geekyshows/Section-20.API Router/2.Tags/ch79/app/user/routers.py @@ -0,0 +1,29 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/users", tags=["users"]) +async def get_all_users(): + return {"data": "All Users"} + +@router.get("/users/me", tags=["users"]) +async def get_current_user(): + return {"data": "Current User"} + +@router.get("/users/{user_id}", tags=["custom"]) +async def get_single_user(user_id: int): + return {"data": "Single User"} + +# router = APIRouter(tags=["users"]) + +# @router.get("/users") +# async def get_all_users(): +# return {"data": "All Users"} + +# @router.get("/users/me") +# async def get_current_user(): +# return {"data": "Current User"} + +# @router.get("/users/{user_id}", tags=["custom"]) +# async def get_single_user(user_id: int): +# return {"data": "Single User"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/2.Tags/ch79/requirements.txt b/Geekyshows/Section-20.API Router/2.Tags/ch79/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/3.Prefix/README.md b/Geekyshows/Section-20.API Router/3.Prefix/README.md new file mode 100644 index 0000000..5fac9a6 --- /dev/null +++ b/Geekyshows/Section-20.API Router/3.Prefix/README.md @@ -0,0 +1,57 @@ +- [FastAPI API Router Doc](https://fastapi.tiangolo.com/reference/apirouter/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/__init__.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/main.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/main.py new file mode 100644 index 0000000..535c50c --- /dev/null +++ b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/main.py @@ -0,0 +1,12 @@ +from fastapi import FastAPI +from app.user.routers import router as user_routers +from app.product.routers import router as product_routers + +app = FastAPI() + +app.include_router(user_routers, prefix="/users") +app.include_router(product_routers) + +@app.get("/") +async def root(): + return {"data": "Root"} diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/product/__init__.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/product/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/product/routers.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/product/routers.py new file mode 100644 index 0000000..1bb0172 --- /dev/null +++ b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/product/routers.py @@ -0,0 +1,11 @@ +from fastapi import APIRouter + +router = APIRouter(prefix="/products") + +@router.get("/") +async def get_all_products(): + return {"data": "All Products"} + +@router.get("/{product_id}") +async def get_single_product(product_id: int): + return {"data": "Single Product"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/user/__init__.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/user/routers.py b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/user/routers.py new file mode 100644 index 0000000..c538e49 --- /dev/null +++ b/Geekyshows/Section-20.API Router/3.Prefix/ch80/app/user/routers.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter + +router = APIRouter(tags=["users"]) + +@router.get("/") +async def get_all_users(): + return {"data": "All Users"} + +@router.get("/me") +async def get_current_user(): + return {"data": "Current User"} + +@router.get("/{user_id}") +async def get_single_user(user_id: int): + return {"data": "Single User"} \ No newline at end of file diff --git a/Geekyshows/Section-20.API Router/3.Prefix/ch80/requirements.txt b/Geekyshows/Section-20.API Router/3.Prefix/ch80/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/README.md b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/README.md new file mode 100644 index 0000000..040eb3e --- /dev/null +++ b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/README.md @@ -0,0 +1,57 @@ +- [FastAPI Dependencies Doc](https://fastapi.tiangolo.com/tutorial/dependencies/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` + +- Try HTTPSRedirectMiddleware + + ```sh + curl -i http://127.0.0.1:8000/users + ``` + +- Try TrustedHostMiddleware + + ```sh + curl -H "Host: localhost" http://127.0.0.1:8000/users + ``` + + ```sh + curl -H "Host: example.com" http://127.0.0.1:8000/users + ``` \ No newline at end of file diff --git a/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/app/__init__.py b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/app/main.py b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/app/main.py new file mode 100644 index 0000000..816a1a6 --- /dev/null +++ b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/app/main.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI, Depends +from typing import Annotated + +app = FastAPI() + +# Creating Dependency Function +async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100): + return {"q": q, "skip": skip, "limit": limit} + +# Using Dependency in endpoints +@app.get("/items") +async def read_items(commons: Annotated[dict, Depends(common_parameters)]): + return commons + +@app.get("/users") +async def read_users(commons: Annotated[dict, Depends(common_parameters)]): + return commons + +# Create a type alias +CommonsDep = Annotated[dict, Depends(common_parameters)] + +@app.get("/products") +async def read_products(commons: CommonsDep): + return commons + +@app.get("/carts") +async def read_carts(commons: CommonsDep): + return commons \ No newline at end of file diff --git a/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/requirements.txt b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/requirements.txt new file mode 100644 index 0000000..ab9e088 --- /dev/null +++ b/Geekyshows/Section-21.Dependency Injection/1.Creating and Using Dependency Injection/ch81/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.1 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.6 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.7 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.16.0 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +uvicorn==0.34.3 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/README.md new file mode 100644 index 0000000..8857f92 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters Doc](https://fastapi.tiangolo.com/tutorial/path-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/app/main.py new file mode 100644 index 0000000..4ccb8db --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/app/main.py @@ -0,0 +1,38 @@ +from fastapi import FastAPI + +app = FastAPI() + +# GET Request +## Read or Fetch All Data +@app.get("/product") +async def all_products(): + return {"response": "All Products"} + +## Read or Fetch Single Data +@app.get("/product/{product_id}") +async def single_product(product_id:int): + return {"response":"Single Data Fetched", "product_id": product_id} + +# POST Request +## Create or Insert Data +@app.post("/product") +async def create_product(new_product: dict): + return {"response": "Product Created", "new product": new_product} + +# PUT Request +## Update Complete Data +@app.put("/product/{product_id}") +async def update_product(new_updated_product: dict, product_id: int): + return {"response":"Complete Data Updated", "product_id": product_id, "new updated product":new_updated_product} + +# PATCH Request +## Update Partial Data +@app.patch("/product/{product_id}") +async def partial_product(new_updated_product: dict, product_id: int): + return {"response":"Partial Data Updated", "product_id": product_id, "new updated product":new_updated_product} + +# DELETE Request +## Delete Data +@app.delete("/product/{product_id}") +def delete_product(product_id: int): + return {"response":"Data Deleted", "product_id": product_id} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/1.Path Parameters and HTTP Methods/ch5/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/README.md new file mode 100644 index 0000000..c5dd96e --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters and Numeric Validations Doc](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/app/main.py new file mode 100644 index 0000000..435635f --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/app/main.py @@ -0,0 +1,46 @@ +from fastapi import FastAPI, Path, Query +from typing import Annotated +app = FastAPI() + +PRODUCTS = [ + {"id": 1, "title": "Ravan Backpack", "price": 109.95, "description": "Perfect for everyday use and forest walks."}, + {"id": 2, "title": "Slim Fit T-Shirts", "price": 22.3, "description": "Comfortable, slim-fitting casual shirts."}, + {"id": 3, "title": "Cotton Jacket", "price": 55.99, "description": "Great for outdoor activities and gifting."}, +] + +# # Basic Path Parameter +@app.get("/products/{product_id}") +async def get_product(product_id: int): + for product in PRODUCTS: + if product["id"] == product_id: + return product + return {"error": "Product not found"} + +# # Numeric Validation +# @app.get("/products/{product_id}") +# async def get_product(product_id: Annotated[int, Path(ge=1, le=3)]): +# for product in PRODUCTS: +# if product["id"] == product_id: +# return product +# return {"error": "Product not found"} + +## Adding Metadata with Path +# @app.get("/products/{product_id}") +# async def get_product(product_id: Annotated[int, Path(title="The ID of the product", description="This is product id")]): +# for product in PRODUCTS: +# if product["id"] == product_id: +# return product +# return {"error": "Product not found"} + +## Combining Path and Query Parameters +# @app.get("/products/{product_id}") +# async def get_product( +# product_id: Annotated[int, Path(gt=0, le=100)], +# search: Annotated[str | None, Query(max_length=20)] = None +# ): +# for product in PRODUCTS: +# if product["id"] == product_id: +# if search and search.lower() not in product["title"].lower(): +# return {"error": "Product does not match search term"} +# return product +# return {"error": "Product not found"} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/10.Path Parameter Validation/ch14/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/README.md new file mode 100644 index 0000000..b84ec12 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path parameters with types Doc](https://fastapi.tiangolo.com/tutorial/path-params/#path-parameters-with-types) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/app/main.py new file mode 100644 index 0000000..854628f --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/app/main.py @@ -0,0 +1,16 @@ +from fastapi import FastAPI + +app = FastAPI() + +## Parameter with Type +@app.get("/product/{product_id}") +async def single_product(product_id): + return {"response":"Single Data Fetched", "product_id": product_id} + +# @app.get("/product/{product_id}") +# async def single_product(product_id:int): +# return {"response":"Single Data Fetched", "product_id": product_id} + +# @app.get("/product/{product_title}") +# async def single_product(product_title:str): +# return {"response":"Single Data Fetched", "product_title": product_title} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/2.Path Parameter with Type/ch6/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/README.md new file mode 100644 index 0000000..59e7d2a --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters Order matters Doc](https://fastapi.tiangolo.com/tutorial/path-params/#order-matters) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/app/main.py new file mode 100644 index 0000000..688e75b --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/app/main.py @@ -0,0 +1,13 @@ +from fastapi import FastAPI + +app = FastAPI() + +## Order matters + +@app.get("/product/rode_nt_usb") +async def single_product(): + return {"response":"Single Data Fetched"} + +@app.get("/product/{product_title}") +async def single_product(product_title:str): + return {"response":"Single Data Fetched", "product_title": product_title} diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/3.Path Parameter order matters/ch7/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/README.md new file mode 100644 index 0000000..e143670 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters Predefined values Doc](https://fastapi.tiangolo.com/tutorial/path-params/#predefined-values) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/app/main.py new file mode 100644 index 0000000..9596380 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/app/main.py @@ -0,0 +1,32 @@ +from fastapi import FastAPI +from enum import Enum +app = FastAPI() + +## Predefined values +# Define an Enum class with allowed product categories +class ProductCategory(str, Enum): + books = "books" + clothing = "clothing" + electronics = "electronics" + +# Use the Enum as the type for the path parameter +@app.get("/product/{category}") +async def get_products(category:ProductCategory): + return {"response": "Products fetched", "category": category} + +## Working with Python enumerations +# class ProductCategory(str, Enum): +# books = "books" +# clothing = "clothing" +# electronics = "electronics" + +# @app.get("/product/{category}") +# async def get_products(category:ProductCategory): +# if category == ProductCategory.books: +# return {"category": category, "message": "Books are awesome!"} +# elif category.value == "clothing": +# return {"category": category, "message": "Fashion trends here!"} +# elif category == ProductCategory.electronics.value: +# return {"category": category, "message": "Latest gadgets available!"} +# else: +# return {"category": category, "message": "Unknown category"} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/4.Path Parameter Predefined Value/ch8/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/README.md new file mode 100644 index 0000000..b7925bc --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters Path Convertor Doc](https://fastapi.tiangolo.com/tutorial/path-params/#path-convertor) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/app/main.py new file mode 100644 index 0000000..c87e082 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/app/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI() + +# :path convertor +@app.get("/files/{file_path:path}") +async def read_file(file_path: str): + return {"You requested file at path": file_path} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/5.Path converter/ch9/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/README.md new file mode 100644 index 0000000..8857f92 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/README.md @@ -0,0 +1,41 @@ +- [FastAPI Path Parameters Doc](https://fastapi.tiangolo.com/tutorial/path-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/app/main.py new file mode 100644 index 0000000..0471c08 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/app/main.py @@ -0,0 +1,72 @@ +from fastapi import FastAPI + +app = FastAPI() + +PRODUCTS = [ + { + "id": 1, + "title": "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops", + "price": 109.95, + "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday" + }, + { + "id": 2, + "title": "Mens Casual Premium Slim Fit T-Shirts ", + "price": 22.3, + "description": "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket." + }, + { + "id": 3, + "title": "Mens Cotton Jacket", + "price": 55.99, + "description": "great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day." + }, + ] + +# GET Request +## Read or Fetch All Data +@app.get("/product") +async def all_products(): + return PRODUCTS + +## Read or Fetch Single Data +@app.get("/product/{product_id}") +async def single_products(product_id:int): + for product in PRODUCTS: + if product["id"] == product_id: + return product + +# POST Request +## Create or Insert Data +@app.post("/product") +async def create_product(new_product: dict): + PRODUCTS.append(new_product) + return {"status":"created", "new_product":new_product} + +# PUT Request +## Update Complete Data +@app.put("/product/{product_id}") +def update_product(product_id: int, new_updated_product: dict): + for index, product in enumerate(PRODUCTS): + if product["id"] == product_id: + PRODUCTS[index] = new_updated_product + return {"status": "Updated", "product_id": product_id, "new updated product": new_updated_product} + + +# PATCH Request +## Update Partial Data +@app.patch("/product/{product_id}") +def partial_product(product_id: int, new_updated_product: dict): + for product in PRODUCTS: + if product["id"] == product_id: + product.update(new_updated_product) + return {"status": "Partial updated", "product_id": product_id, "new updated product": product} + +# DELETE Request +## Delete Data +@app.delete("/product/{product_id}") +def delete_product(product_id: int): + for index, product in enumerate(PRODUCTS): + if product["id"] == product_id: + PRODUCTS.pop(index) + return {"status": "Deleted", "product_id": product_id} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/6.Your First Fast API CRUD/ch10/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/README.md new file mode 100644 index 0000000..76e6cfa --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/README.md @@ -0,0 +1,41 @@ +- [FastAPI Query Parameters Doc](https://fastapi.tiangolo.com/tutorial/query-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/app/main.py new file mode 100644 index 0000000..d250529 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/app/main.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI + +app = FastAPI() + +# Single Query Parameter +@app.get("/product") +async def product(category:str): + return {"status":"OK", "category":category} + +# # Multiple Query Parameter +# @app.get("/product") +# async def product(category:str, limit:int): +# return {"status":"OK", "category":category, "limit":limit} + +# # Default Query Parameter +# @app.get("/product") +# async def product(category:str, limit:int=10): +# return {"status":"OK", "category":category, "limit":limit} + +# # Optional Query Parameter +# @app.get("/product") +# async def product(limit:int, category:str | None = None): +# return {"status":"OK", "category":category, "limit":limit} + +# Path and Query parameter +# @app.get("/product/{year}") +# async def product(year:str, category:str): +# return {"status":"OK", "year":year, "category":category} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/7.Query Parameter/ch11/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/README.md new file mode 100644 index 0000000..79f82a0 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/README.md @@ -0,0 +1,41 @@ +- [FastAPI Response Status Code Doc](https://fastapi.tiangolo.com/tutorial/response-status-code/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/app/main.py new file mode 100644 index 0000000..618977e --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/app/main.py @@ -0,0 +1,72 @@ +from fastapi import FastAPI, status + +app = FastAPI() + +PRODUCTS = [ + { + "id": 1, + "title": "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops", + "price": 109.95, + "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday" + }, + { + "id": 2, + "title": "Mens Casual Premium Slim Fit T-Shirts ", + "price": 22.3, + "description": "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket." + }, + { + "id": 3, + "title": "Mens Cotton Jacket", + "price": 55.99, + "description": "great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day." + }, + ] + +# GET Request +## Read or Fetch All Data +@app.get("/product", status_code=status.HTTP_200_OK) +async def all_products(): + return PRODUCTS + +## Read or Fetch Single Data +@app.get("/product/{product_id}", status_code=status.HTTP_200_OK) +async def single_products(product_id:int): + for product in PRODUCTS: + if product["id"] == product_id: + return product + +# POST Request +## Create or Insert Data +@app.post("/product", status_code=status.HTTP_201_CREATED) +async def create_product(new_product: dict): + PRODUCTS.append(new_product) + return {"status":"created", "new_product":new_product} + +# PUT Request +## Update Complete Data +@app.put("/product/{product_id}", status_code=status.HTTP_200_OK) +def update_product(product_id: int, new_updated_product: dict): + for index, product in enumerate(PRODUCTS): + if product["id"] == product_id: + PRODUCTS[index] = new_updated_product + return {"status": "Updated", "product_id": product_id, "new updated product": new_updated_product} + + +# PATCH Request +## Update Partial Data +@app.patch("/product/{product_id}", status_code=status.HTTP_200_OK) +def partial_product(product_id: int, new_updated_product: dict): + for product in PRODUCTS: + if product["id"] == product_id: + product.update(new_updated_product) + return {"status": "Partial updated", "product_id": product_id, "new updated product": product} + +# DELETE Request +## Delete Data +@app.delete("/product/{product_id}", status_code=status.HTTP_200_OK) +def delete_product(product_id: int): + for index, product in enumerate(PRODUCTS): + if product["id"] == product_id: + PRODUCTS.pop(index) + return {"status": "Deleted", "product_id": product_id} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/8.Response Status Code/ch12/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/README.md b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/README.md new file mode 100644 index 0000000..14ddd0d --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/README.md @@ -0,0 +1,41 @@ +- [FastAPI Query Parameters and String Validations Doc](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/app/main.py b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/app/main.py new file mode 100644 index 0000000..eab59b4 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/app/main.py @@ -0,0 +1,150 @@ +from fastapi import FastAPI, Query +from typing import Annotated +from pydantic import AfterValidator + +app = FastAPI() + +PRODUCTS = [ + {"id": 1, "title": "Ravan Backpack", "price": 109.95, "description": "Perfect for everyday use and forest walks."}, + {"id": 2, "title": "Slim Fit T-Shirts", "price": 22.3, "description": "Comfortable, slim-fitting casual shirts."}, + {"id": 3, "title": "Cotton Jacket", "price": 55.99, "description": "Great for outdoor activities and gifting."}, +] + +# Basic Query Parameter +@app.get("/products") +async def get_products(search:str | None = None): + if search: + search_lower = search.lower() + filtered_products = [] + for product in PRODUCTS: + if search_lower in product["title"].lower(): + filtered_products.append(product) + return filtered_products + return PRODUCTS + +# Validation without Annotated (Old way - not recommended) +# @app.get("/products") +# async def get_products(search:str | None = Query(default=None, max_length=5)): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +# Validation with Annotated +# @app.get("/products") +# async def get_products( +# search: +# Annotated[ +# str | None, +# Query(max_length=5) +# ] = None): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +# Why use Annotated +## Clear separation of the type +## Better support in some editors and tools for showing metadata and validations directly in the type hints +## Requires Python 3.9+ and FastAPI 0.95+; more modern and recommended approach +## FastAPI 0.95+ officially recommends using Annotated for dependencies and parameters + +# # Required Parameter +# @app.get("/products/") +# async def get_products(search: Annotated[str, Query(min_length=3)]): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +## Add regular expressions +# @app.get("/products/") +# async def get_products(search: Annotated[str | None, Query(min_length=3, pattern="^[a-z]+$")] = None): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +# # Multiple Search Terms (List) +# @app.get("/products") +# async def get_products(search: Annotated[list[str] | None, Query()] = None): +# if search: +# filtered_products = [] +# for product in PRODUCTS: +# for s in search: +# if s.lower() in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +## Alias parameters +# @app.get("/products/") +# async def get_products(search: Annotated[str | None, Query(alias="q")] = None): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +# # Adding Metadata +# @app.get("/products/") +# async def get_products(search: Annotated[ +# str | None, +# Query(alias="q", title="Search Products", description="Search by product title") +# ] = None +# ): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +# # Deprecating parameters +# @app.get("/products/") +# async def get_products(search: Annotated[ +# str | None, +# Query(deprecated=True) +# ] = None +# ): +# if search: +# search_lower = search.lower() +# filtered_products = [] +# for product in PRODUCTS: +# if search_lower in product["title"].lower(): +# filtered_products.append(product) +# return filtered_products +# return PRODUCTS + +## Custom Validation +# def check_valid_id(id: str): +# if not id.startswith("prod-"): +# raise ValueError("ID must start with 'prod-'") +# return id + +# @app.get("/products/") +# async def get_products(id: Annotated[str | None, AfterValidator(check_valid_id)] = None): +# if id: +# return {"id": id, "message": "Valid product ID"} +# return {"message": "No ID provided"} \ No newline at end of file diff --git a/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/requirements.txt b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-3.Path Parameters and Query Parameters/9.Query Parameter Validation/ch13/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/README.md b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/README.md new file mode 100644 index 0000000..aa40054 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Request Body Doc](https://fastapi.tiangolo.com/tutorial/body/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/app/main.py b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/app/main.py new file mode 100644 index 0000000..8ede024 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/app/main.py @@ -0,0 +1,49 @@ +from fastapi import FastAPI +from pydantic import BaseModel +app = FastAPI() + +# ## Without Pydantic +# ## Create or Insert Data +# @app.post("/product") +# async def create_product(new_product: dict): +# return new_product + + +## With Pydantic +# # Define the Product model +class Product(BaseModel): + id: int + name: str + price: float + stock: int | None = None + +@app.post("/product") +async def create_product(new_product: Product): + return new_product + +# ## Access Attribute inside Function +# @app.post("/product") +# async def create_product(new_product: Product): +# print(new_product.id) +# print(new_product.name) +# print(new_product.price) +# print(new_product.stock) +# return new_product + +## Add new calculated attribute +# @app.post("/product") +# async def create_product(new_product: Product): +# product_dict = new_product.model_dump() +# price_with_tax = new_product.price + (new_product.price * 18 / 100) +# product_dict.update({"price_with_tax": price_with_tax}) +# return product_dict + +## Combining Request Body with Path Parameters +# @app.put("/products/{product_id}") +# async def update_product(product_id: int, new_updated_product: Product): +# return {"product_id": product_id, "new_updated_product":new_updated_product} + +## Adding Query Parameters +# @app.put("/products/{product_id}") +# async def update_product(product_id: int, new_updated_product: Product, discount: float | None = None): +# return {"product_id": product_id, "new_updated_product": new_updated_product, "discount": discount} \ No newline at end of file diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/requirements.txt b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/1.Validate Request Body using Pydantic Model/ch15/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/README.md b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/README.md new file mode 100644 index 0000000..958e045 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/README.md @@ -0,0 +1,41 @@ +- [FastAPI Body - Multiple Parameters Doc](https://fastapi.tiangolo.com/tutorial/body-multiple-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/app/main.py b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/app/main.py new file mode 100644 index 0000000..197a3a6 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/app/main.py @@ -0,0 +1,43 @@ +from fastapi import FastAPI, Body +from pydantic import BaseModel +from typing import Annotated +app = FastAPI() + +## Multiple Body Parameters +class Product(BaseModel): + name: str + price:float + stock: int | None = None + +class Seller(BaseModel): + username: str + full_name: str | None = None + +@app.post("/product") +async def create_product(product: Product, seller:Seller): + return {"product": product, "seller":seller} + +## Make Body Optional +# @app.post("/product") +# async def create_product(product: Product, seller:Seller | None = None): +# return {"product": product, "seller":seller} + +## Singular values in body +# @app.post("/product") +# async def create_product( +# product: Product, +# seller:Seller, +# sec_key: Annotated[str, Body()] +# ): +# return {"product": product, "seller":seller, "sec_key":sec_key} + +## Embed a single body parameter +## Without Embed +# @app.post("/product") +# async def create_product(product: Product): +# return product + +## With Embed +# @app.post("/product") +# async def create_product(product: Annotated[Product, Body(embed=True)]): +# return product \ No newline at end of file diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/requirements.txt b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/2.Multiple Body Parameters/ch16/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/README.md b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/README.md new file mode 100644 index 0000000..61d4118 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/README.md @@ -0,0 +1,41 @@ +- [FastAPI Body - Fields Doc](https://fastapi.tiangolo.com/tutorial/body-fields) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/app/main.py b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/app/main.py new file mode 100644 index 0000000..cb1dc0e --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/app/main.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI +from pydantic import BaseModel, Field +app = FastAPI() + +## Pydantic’s Field +class Product(BaseModel): + name: str = Field( + title="Product Name", + description="The name of the product", + max_length=100, + min_length=3, + pattern="^[A-Za-z0-9 ]+$" + ) + price: float = Field( + gt=0, + title="Product Price", + description="The price of the product in USD, must be greater than zero" + ) + stock: int | None = Field( + default=None, + ge=0, + title="Stock Quantity", + description="The number of items in stock, must be non-negative" + ) + +@app.post("/product") +async def create_product(product: Product): + return product \ No newline at end of file diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/requirements.txt b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/3.Pydantic Field/ch17/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/README.md b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/README.md new file mode 100644 index 0000000..8623e94 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Body - Nested Models Doc](https://fastapi.tiangolo.com/tutorial/body-nested-models/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/app/main.py b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/app/main.py new file mode 100644 index 0000000..f5f8188 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/app/main.py @@ -0,0 +1,92 @@ +from fastapi import FastAPI +from pydantic import BaseModel, Field + +app = FastAPI() + +# ## Nested Body Models +## Submodel +class Category(BaseModel): + name : str = Field( + title="Category Name", + description="The name of the product category", + max_length=50, + min_length=1 + ) + description: str | None = Field( + default=None, + title="Category Description", + description="A brief description of the category", + max_length=200 + ) + +## Model which will use Submodel +class Product(BaseModel): + name: str = Field( + title="Product Name", + description="The name of the product", + max_length=100, + min_length=1 + ) + price: float = Field( + gt=0, + title="Product Price", + description="The price in USD, must be greater than zero" + ) + stock: int | None = Field( + default=None, + ge=0, + title="Stock Quantity", + description="Number of items in stock, must be non-negative" + ) + category : Category | None = Field( + default=None, + title="Product Category", + description="The category to which the product belongs" + ) + +@app.post("/products") +async def create_product(product: Product): + return product + +# # ## Attributes with lists of submodels +# class Category(BaseModel): +# name: str = Field( +# title="Category Name", +# description="The name of the product category", +# max_length=50, +# min_length=1 +# ) +# description: str | None = Field( +# default=None, +# title="Category Description", +# description="A brief description of the category", +# max_length=200 +# ) + +# class Product(BaseModel): +# name: str = Field( +# title="Product Name", +# description="The name of the product", +# max_length=100, +# min_length=1 +# ) +# price: float = Field( +# gt=0, +# title="Product Price", +# description="The price in USD, must be greater than zero" +# ) +# stock: int | None = Field( +# default=None, +# ge=0, +# title="Stock Quantity", +# description="Number of items in stock, must be non-negative" +# ) +# category: list[Category] | None = Field( +# default=None, +# title="Product Category", +# description="The category to which the product belongs" +# ) + +# @app.post("/products") +# async def create_product(product: Product): +# return product \ No newline at end of file diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/requirements.txt b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/4.Nested Pydantic Body Model/ch18/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/README.md b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/README.md new file mode 100644 index 0000000..95d8600 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/README.md @@ -0,0 +1,41 @@ +- [FastAPI Declare Request Example Data Doc](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/app/main.py b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/app/main.py new file mode 100644 index 0000000..b28bf17 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/app/main.py @@ -0,0 +1,36 @@ +from fastapi import FastAPI +from pydantic import BaseModel, Field + +app = FastAPI() + +# ## Using Field-level Examples +# class Product(BaseModel): +# name: str = Field(examples=["Moto E"]) +# price: float = Field(examples=[23.56]) +# stock: int | None = Field(default=None, examples=[43]) + +# @app.post("/products") +# async def create_product(product: Product): +# return product + +## Using Pydantic’s json_schema_extra +class Product(BaseModel): + name: str + price: float + stock: int | None = None + + model_config = { + "json_schema_extra": { + "examples": [ + { + "name": "Moto E", + "price": 34.56, + "stock": 45 + } + ] + } + } + +@app.post("/products") +async def create_product(product: Product): + return product \ No newline at end of file diff --git a/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/requirements.txt b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-4.Request Body and Pydantic Model/5.Pydantic Body Example Value/ch19/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/README.md b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/README.md new file mode 100644 index 0000000..2e1b409 --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/README.md @@ -0,0 +1,41 @@ +- [FastAPI Cookie Parameters Doc](https://fastapi.tiangolo.com/tutorial/cookie-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/app/main.py b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/app/main.py new file mode 100644 index 0000000..024b0c1 --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/app/main.py @@ -0,0 +1,13 @@ +from fastapi import FastAPI, Cookie +from typing import Annotated +app = FastAPI() + +## Cookie Parameters +@app.get("/products/recommendations") +async def get_recommendations(session_id: Annotated[str | None, Cookie()] = None): + if session_id: + return {"message": f"Recommendations for session {session_id}", "session_id": session_id} + return {"message": "No session ID provided, showing default recommendations"} + +# To test this endpoint, you can use curl or any HTTP client. +# curl -H "Cookie: session_id=abc123" http://127.0.0.1:8000/products/recommendations \ No newline at end of file diff --git a/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/requirements.txt b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/1.Cookie Parameter/ch20/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/README.md b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/README.md new file mode 100644 index 0000000..84ea61b --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Cookie Parameter Models Doc](https://fastapi.tiangolo.com/tutorial/cookie-param-models/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/app/main.py b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/app/main.py new file mode 100644 index 0000000..092957f --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/app/main.py @@ -0,0 +1,69 @@ +from fastapi import FastAPI, Cookie, Body +from typing import Annotated +from pydantic import BaseModel, Field +app = FastAPI() + +# ## Cookies with a Pydantic Model +# class ProductCookies(BaseModel): +# session_id: str +# preferred_category: str | None = None +# tracking_id: str | None = None + +# @app.get("/products/recommendations") +# async def get_recommendations(cookies: Annotated[ProductCookies, Cookie()]): +# response = {"session_id": cookies.session_id} +# if cookies.preferred_category: +# response["message"] = f"Recommendations for {cookies.preferred_category} products" +# else: +# response["message"] = f"Default recommendations for session {cookies.session_id}" +# if cookies.tracking_id: +# response["tracking_id"] = cookies.tracking_id +# return response + +# curl -H "Cookie: session_id=abc123; preferred_category=Electronics; tracking_id=xyz789" http://127.0.0.1:8000/products/recommendations + +## Forbidding Extra Cookies +# class ProductCookies(BaseModel): +# model_config = {"extra": "forbid"} +# session_id: str +# preferred_category: str | None = None +# tracking_id: str | None = None + +# @app.get("/products/recommendations") +# async def get_recommendations(cookies: Annotated[ProductCookies, Cookie()]): +# response = {"session_id": cookies.session_id} +# if cookies.preferred_category: +# response["message"] = f"Recommendations for {cookies.preferred_category} products" +# else: +# response["message"] = f"Default recommendations for session {cookies.session_id}" +# if cookies.tracking_id: +# response["tracking_id"] = cookies.tracking_id +# return response + +# Combining Cookie with Body Parameters +class ProductCookies(BaseModel): + model_config = {"extra": "forbid"} + session_id: str = Field(title="Session ID", description="User session identifier") + preferred_category: str | None = Field(default=None, title="Preferred Category", description="User's preferred product category") + +class PriceFilter(BaseModel): + min_price: float = Field(ge=0, title="Minimum Price", description="Minimum price for recommendations") + max_price: float | None = Field(default=None, title="Maximum Price", description="Maximum price for recommendations") + +@app.post("/products/recommendations") +async def get_recommendations( + cookies: Annotated[ProductCookies, Cookie()], + price_filter: Annotated[PriceFilter, Body(embed=True)] + ): + response = {"session_id": cookies.session_id} + if cookies.preferred_category: + response["category"] = cookies.preferred_category + response["price_range"] = { + "min_price": price_filter.min_price, + "max_price": price_filter.max_price + } + response["message"] = f"Recommendations for session {cookies.session_id} with price range {price_filter.min_price} to {price_filter.max_price or 'unlimited'}" + return response + +# To test the endpoint, you can use the following curl command: +# curl -X POST -H "Cookie: session_id=abc123; preferred_category=Electronics" -H "Content-Type: application/json" -d "{\"price_filter\":{\"min_price\":50.0,\"max_price\":1000.0}}" http://127.0.0.1:8000/products/recommendations \ No newline at end of file diff --git a/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/requirements.txt b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-5.Cookie Parameters/2.Cookie Parameter with Pydantic Model/ch21/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-6.Header Parameters/1.Header Parameter/README.md b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/README.md new file mode 100644 index 0000000..f7c55e2 --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/README.md @@ -0,0 +1,41 @@ +- [FastAPI Header Parameters Doc](https://fastapi.tiangolo.com/tutorial/header-params/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/app/main.py b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/app/main.py new file mode 100644 index 0000000..3ccdf0c --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/app/main.py @@ -0,0 +1,19 @@ +from fastapi import FastAPI, Header +from typing import Annotated +app = FastAPI() + +## Header Parameters +@app.get("/products") +async def get_products(user_agent: Annotated[str|None, Header()] = None): + return user_agent + +# curl -H "User-Agent: Mozilla/5.0" http://127.0.0.1:8000/products + +## Handling Duplicate Headers +# @app.get("/products") +# async def get_product(x_product_token: Annotated[list[str] | None, Header()] = None): +# return { +# "x_product_token": x_product_token or [] +# } + +# curl -H "X-Product-Token: token1" -H "X-Product-Token: token2" http://127.0.0.1:8000/products \ No newline at end of file diff --git a/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/requirements.txt b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/1.Header Parameter/ch22/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/README.md b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/README.md new file mode 100644 index 0000000..f16bfc6 --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Header Parameter Models Doc](https://fastapi.tiangolo.com/tutorial/header-param-models/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/app/main.py b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/app/main.py new file mode 100644 index 0000000..b332f0e --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/app/main.py @@ -0,0 +1,34 @@ +from typing import Annotated +from fastapi import FastAPI, Header, Body +from pydantic import BaseModel, Field +app = FastAPI() + +## Headers with a Pydantic Model + +class ProductHeaders(BaseModel): + authorization: str + accept_language: str | None = None + x_tracking_id: list[str] = [] + +@app.get("/products") +async def get_product(headers: Annotated[ProductHeaders, Header()]): + return { + "headers": headers + } + +# curl -H "Authorization: Bearer token123" -H "Accept-Language: en-US" -H "X-Tracking-Id: track1" -H "X-Tracking-Id: track2" http://127.0.0.1:8000/products + +# Forbidding Extra Headers +# class ProductHeaders(BaseModel): +# model_config = {"extra":"forbid"} +# authorization: str +# accept_language: str | None = None +# x_tracking_id: list[str] = [] + +# @app.get("/products") +# async def get_product(headers: Annotated[ProductHeaders, Header()]): +# return { +# "headers": headers +# } + +# curl -H "Authorization: Bearer token123" -H "Accept-Language: en-US" -H "X-Tracking-Id: track1" -H "X-Tracking-Id: track2" -H "extra-header: h123" http://127.0.0.1:8000/products \ No newline at end of file diff --git a/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/requirements.txt b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-6.Header Parameters/2.Header Parameter with Pydantic Model/ch23/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-7.Return Type/1.Return type annotation/README.md b/Geekyshows/Section-7.Return Type/1.Return type annotation/README.md new file mode 100644 index 0000000..d4644c6 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/1.Return type annotation/README.md @@ -0,0 +1,41 @@ +- [FastAPI Response Model - Return Type Doc](https://fastapi.tiangolo.com/tutorial/response-model/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/app/main.py b/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/app/main.py new file mode 100644 index 0000000..b8048ea --- /dev/null +++ b/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/app/main.py @@ -0,0 +1,63 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from typing import List, Any + +app = FastAPI() + +class Product(BaseModel): + id: int + name: str + price : float + stock: int | None = None + +class ProductOut(BaseModel): + name: str + price : float + +## without response_model Parameter +# @app.get("/products/") +# async def get_products(): +# return {"id": 1, "name": "Moto E", "price": 33.44, "stock": 5} + +# with response_model Parameter +@app.get("/products/", response_model=Product) +async def get_products(): + return {"id": 1, "name": "Moto E", "price": 33.44, "stock": 5} + + +# @app.get("/products/", response_model=List[Product]) +# async def get_products(): +# return [ +# {"id": 1, "name": "Moto E", "price": 33.44, "stock": 5}, +# {"id": 2, "name": "Redmi 4", "price": 55.33, "stock": 7} +# ] + +# @app.get("/products/", response_model=List[Product]) +# async def get_products(): +# return [ +# {"id": 1, "name": "Moto E", "price": 33.44, "stock": 5, "description": "Hello Desc1"}, +# {"id": 2, "name": "Redmi 4", "price": 55.33, "stock": 7, "description": "Hello Desc2"} +# ] + +# @app.post("/products/", response_model=Product) +# async def create_product(product: Product): +# return product + +# class BaseUser(BaseModel): +# username: str +# full_name: str | None = None + +# class UserIn(BaseUser): +# password: str + +# @app.post("/users/", response_model=BaseUser) +# async def create_user(user: UserIn): +# return user + +# @app.post("/products/", response_model=Product) +# async def create_product(product: Product) -> Any: +# return product + +# @app.post("/products/", response_model=None) +# async def create_product(product: Product) -> Any: +# return product \ No newline at end of file diff --git a/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/requirements.txt b/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/1.Return type annotation/ch25/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-7.Return Type/2.Response Model/README.md b/Geekyshows/Section-7.Return Type/2.Response Model/README.md new file mode 100644 index 0000000..d4644c6 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/2.Response Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Response Model - Return Type Doc](https://fastapi.tiangolo.com/tutorial/response-model/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-7.Return Type/2.Response Model/ch26/app/main.py b/Geekyshows/Section-7.Return Type/2.Response Model/ch26/app/main.py new file mode 100644 index 0000000..1ff68f7 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/2.Response Model/ch26/app/main.py @@ -0,0 +1,33 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from typing import List, Any, Optional + +app = FastAPI() + +## Excluding Unset Default Values + +products_db = { + "1": {"id": "1", "name": "Laptop", "price": 999.99, "stock": 10, "is_active": True}, + "2": {"id": "2", "name": "Smartphone", "price": 499.99, "stock": 50, "is_active": False} +} + +class Product(BaseModel): + id: str + name: str + price: float + description: Optional[str] = None + tax: float = 15.0 # Default tax rate + +@app.get("/products/{product_id}", response_model=Product, response_model_exclude_unset=True) +async def get_product(product_id: str): + return products_db.get(product_id, {}) + +## Including Specific Fields +# @app.get("/products/{product_id}", response_model=Product, response_model_include={"name", "price"}) +# async def get_product(product_id: str): +# return products_db.get(product_id, {}) + +# ## Excluding Specific Fields +# @app.get("/products/{product_id}", response_model=Product, response_model_exclude={"tax", "description"}) +# async def get_product(product_id: str): +# return products_db.get(product_id, {}) \ No newline at end of file diff --git a/Geekyshows/Section-7.Return Type/2.Response Model/ch26/requirements.txt b/Geekyshows/Section-7.Return Type/2.Response Model/ch26/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/2.Response Model/ch26/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/README.md b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/README.md new file mode 100644 index 0000000..d4644c6 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/README.md @@ -0,0 +1,41 @@ +- [FastAPI Response Model - Return Type Doc](https://fastapi.tiangolo.com/tutorial/response-model/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/app/main.py b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/app/main.py new file mode 100644 index 0000000..30c2a65 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/app/main.py @@ -0,0 +1,36 @@ +from fastapi import FastAPI, Form +from fastapi.responses import HTMLResponse +from typing import Annotated +app = FastAPI() + +# application/x-www-form-urlencoded +# multipart/form-data + +# # Simple HTML form for testing +@app.get("/", response_class=HTMLResponse) +async def get_form(): + return """ + + +

Login Form

+
+
+
+
+

+ +
+ + + """ + +@app.post("/login/") +async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]): + return {"username": username, "password_length": len(password)} + +# @app.post("/login/") +# async def login( +# username: Annotated[str, Form(min_length=3)], +# password: Annotated[str, Form(min_length=3, max_length=20)] +# ): +# return {"username": username, "password_length": len(password)} \ No newline at end of file diff --git a/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/requirements.txt b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-7.Return Type/3.Include and Exclude specific field/ch27/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-8.Form Handling/1.Handle Form Data/README.md b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/README.md new file mode 100644 index 0000000..3801830 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/README.md @@ -0,0 +1,41 @@ +- [FastAPI Form Data Doc](https://fastapi.tiangolo.com/tutorial/request-forms/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/app/main.py b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/app/main.py new file mode 100644 index 0000000..4b6f674 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/app/main.py @@ -0,0 +1,42 @@ +from fastapi import FastAPI, Form +from fastapi.responses import HTMLResponse +from typing import Annotated +from pydantic import BaseModel, Field +app = FastAPI() + +# # Simple HTML form for testing +@app.get("/", response_class=HTMLResponse) +async def get_form(): + return """ + + +

Login Form

+
+
+
+
+

+ +
+ + + """ +## Pydantic Models for Forms +class FormData(BaseModel): + username: str + password: str + +# Pydantic Models for Forms with Validation +# class FormData(BaseModel): +# username: str = Field(min_length=3) +# password: str = Field(min_length=3, max_length=20) + +# # Pydantic Models for Forms with Validation +# class FormData(BaseModel): +# username: str = Field(min_length=3) +# password: str = Field(min_length=3, max_length=20) +# model_config = {"extra": "forbid"} + +@app.post("/login/") +async def login(data: Annotated[FormData, Form()]): + return data diff --git a/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/requirements.txt b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/1.Handle Form Data/ch28/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/README.md b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/README.md new file mode 100644 index 0000000..694c4a0 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/README.md @@ -0,0 +1,41 @@ +- [FastAPI Form Models Doc](https://fastapi.tiangolo.com/tutorial/request-form-models/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/app/main.py b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/app/main.py new file mode 100644 index 0000000..4b6f674 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/app/main.py @@ -0,0 +1,42 @@ +from fastapi import FastAPI, Form +from fastapi.responses import HTMLResponse +from typing import Annotated +from pydantic import BaseModel, Field +app = FastAPI() + +# # Simple HTML form for testing +@app.get("/", response_class=HTMLResponse) +async def get_form(): + return """ + + +

Login Form

+
+
+
+
+

+ +
+ + + """ +## Pydantic Models for Forms +class FormData(BaseModel): + username: str + password: str + +# Pydantic Models for Forms with Validation +# class FormData(BaseModel): +# username: str = Field(min_length=3) +# password: str = Field(min_length=3, max_length=20) + +# # Pydantic Models for Forms with Validation +# class FormData(BaseModel): +# username: str = Field(min_length=3) +# password: str = Field(min_length=3, max_length=20) +# model_config = {"extra": "forbid"} + +@app.post("/login/") +async def login(data: Annotated[FormData, Form()]): + return data diff --git a/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/requirements.txt b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/2.Form Data with Pydantic Model/ch28/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-8.Form Handling/3.Single File Upload/README.md b/Geekyshows/Section-8.Form Handling/3.Single File Upload/README.md new file mode 100644 index 0000000..29559dd --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/3.Single File Upload/README.md @@ -0,0 +1,41 @@ +- [FastAPI Request Files Doc](https://fastapi.tiangolo.com/tutorial/request-files/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/main.py b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/main.py new file mode 100644 index 0000000..68c8e5a --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/main.py @@ -0,0 +1,61 @@ +from fastapi import FastAPI, File, UploadFile +from fastapi.responses import HTMLResponse +from typing import Annotated +import os +import uuid +import shutil + +app = FastAPI() + + +# HTML form for testing +@app.get("/", response_class=HTMLResponse) +async def main(): + return """ + + +

Single File Upload (bytes)

+
+ + +
+

Single File Upload (UploadFile)

+
+ + +
+ + + """ + +# @app.post("/files/") +# async def create_file(file: Annotated[bytes | None, File()] = None): +# if not file: +# return {"message": "No file sent"} +# return {"file size": len(file)} + +# @app.post("/files/") +# async def create_file(file: Annotated[bytes | None, File()] = None): +# if not file: +# return {"message": "No file sent"} + +# filename = f"{uuid.uuid4()}.bin" +# save_path = f"uploads/{filename}" + +# os.makedirs("uploads", exist_ok=True) + +# with open(save_path, "wb") as buffer: +# buffer.write(file) + +# return {"file size": len(file)} + +@app.post("/uploadfile/") +async def create_upload_file(file: Annotated[UploadFile | None, File()] = None): + if not file: + return {"message": "No upload file sent"} + + save_path = f"uploads/{file.filename}" + os.makedirs("uploads", exist_ok=True) + with open(save_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + return {"filename": file.filename, "content_type": file.content_type} \ No newline at end of file diff --git a/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/7b894830-7077-4923-8158-9ff5c844b04d.bin b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/7b894830-7077-4923-8158-9ff5c844b04d.bin new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/7b894830-7077-4923-8158-9ff5c844b04d.bin @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/laptop2.jpg b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/laptop2.jpg new file mode 100644 index 0000000..281ed79 Binary files /dev/null and b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/app/uploads/laptop2.jpg differ diff --git a/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/requirements.txt b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/3.Single File Upload/ch29/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/README.md b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/README.md new file mode 100644 index 0000000..29559dd --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/README.md @@ -0,0 +1,41 @@ +- [FastAPI Request Files Doc](https://fastapi.tiangolo.com/tutorial/request-files/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/main.py b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/main.py new file mode 100644 index 0000000..4d6198b --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/main.py @@ -0,0 +1,34 @@ +from fastapi import FastAPI, File, UploadFile +from fastapi.responses import HTMLResponse +from typing import Annotated +import os +import shutil + +app = FastAPI() + + +# HTML form for testing +@app.get("/", response_class=HTMLResponse) +async def main(): + return """ + + +

Multiple Files Upload (UploadFile)

+
+ + +
+ + + """ + +@app.post("/uploadfiles/") +async def create_upload_file(files: Annotated[list[UploadFile], File()]): + save_files = [] + os.makedirs("uploads", exist_ok=True) + for file in files: + save_path = f"uploads/{file.filename}" + with open(save_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + save_files.append({"filename": file.filename}) + return save_files \ No newline at end of file diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop3.jpg b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop3.jpg new file mode 100644 index 0000000..6828ff1 Binary files /dev/null and b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop3.jpg differ diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop5.jpg b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop5.jpg new file mode 100644 index 0000000..59585f8 Binary files /dev/null and b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/laptop5.jpg differ diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/test.txt b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/test.txt new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/app/uploads/test.txt @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/requirements.txt b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/4.Multiple File Upload/ch30/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/README.md b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/README.md new file mode 100644 index 0000000..6ebfd38 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/README.md @@ -0,0 +1,41 @@ +- [FastAPI Request Forms and Files Doc](https://fastapi.tiangolo.com/tutorial/request-forms-and-files/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/main.py b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/main.py new file mode 100644 index 0000000..7db2a29 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/main.py @@ -0,0 +1,43 @@ +from fastapi import FastAPI, File, UploadFile, Form +from fastapi.responses import HTMLResponse +from typing import Annotated +import os +import shutil + +app = FastAPI() + +@app.get("/", response_class=HTMLResponse) +async def get_form(): + return """ + + + User Profile Upload + + +

User Profile Form

+
+
+

+
+

+ +
+ + + """ +@app.post("/user-with-file/") +async def create_user_with_file( + username: Annotated[str, Form()], + file: Annotated[UploadFile | None, File()] = None +): + response = {"username": username} + if file: + save_path = f"uploads/{file.filename}" + os.makedirs("uploads", exist_ok=True) + with open(save_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + response["filename"] = file.filename + return response + + + diff --git a/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/uploads/laptop3.jpg b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/uploads/laptop3.jpg new file mode 100644 index 0000000..6828ff1 Binary files /dev/null and b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/app/uploads/laptop3.jpg differ diff --git a/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/requirements.txt b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-8.Form Handling/5.Form Field and File Upload Together/ch31/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/README.md b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/README.md new file mode 100644 index 0000000..ab2863d --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/README.md @@ -0,0 +1,41 @@ +- [FastAPI Handling Errors Doc](https://fastapi.tiangolo.com/tutorial/handling-errors/) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/app/main.py b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/app/main.py new file mode 100644 index 0000000..16d622e --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/app/main.py @@ -0,0 +1,27 @@ +from fastapi import FastAPI, HTTPException + +app = FastAPI() + +items = { + "apple": "A juicy fruit", + "banana": "A yellow delight" + } + +## Using HTTPException +@app.get("/items/{item_id}") +async def read_item(item_id: str): + if item_id not in items: + raise HTTPException(status_code=404, detail="Item not found") + return items[item_id] + +# # Adding Custom Header +# @app.get("/items/{item_id}") +# async def read_item(item_id: str): +# if item_id not in items: +# raise HTTPException( +# status_code=404, +# detail="Item not found", +# headers={"x-error-type": "itemmissing"} +# ) +# return items[item_id] + diff --git a/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/requirements.txt b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/1. HTTPException and Custom Headers/ch32/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-9.Handling Errors/2.Custom Exception/README.md b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/README.md new file mode 100644 index 0000000..8e104a5 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/README.md @@ -0,0 +1,41 @@ +- [FastAPI Install custom exception handlers Doc](https://fastapi.tiangolo.com/tutorial/handling-errors/#install-custom-exception-handlers) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/app/main.py b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/app/main.py new file mode 100644 index 0000000..defddf3 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/app/main.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI, Request +from fastapi.responses import JSONResponse + +app = FastAPI() + +fruits = { + "apple": "A juicy fruit", + "banana": "A yellow delight" + } + +# Create Exception +class FruitException(Exception): + def __init__(self, fruit_name: str): + self.fruit_name = fruit_name + +# Custom Exception Handler +@app.exception_handler(FruitException) +async def fruit_exception_handler(request: Request, exc: FruitException): + return JSONResponse( + status_code=418, + content={"message": f"{exc.fruit_name} is not valid"} + ) + +@app.get("/fruits/{fruit_name}") +async def read_fruit(fruit_name: str): + if fruit_name not in fruits: + raise FruitException(fruit_name=fruit_name) + return fruits[fruit_name] \ No newline at end of file diff --git a/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/requirements.txt b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/2.Custom Exception/ch33/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/README.md b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/README.md new file mode 100644 index 0000000..ca16102 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/README.md @@ -0,0 +1,41 @@ +- [FastAPI Override the default exception handlers Doc](https://fastapi.tiangolo.com/tutorial/handling-errors/#override-the-default-exception-handlers) + +- Create python virtual environment + + ```bash + python3 -m venv .venv + ``` + +- Activate the virtual environment + + ```bash + source .venv/Scripts/activate + ``` + +- Install FastAPI + + ```bash + pip install "fastapi[standard]" + ``` + +- Run Server + + - Using FastAPI CLI (Recommended) + + ```bash + fastapi dev main.py + ``` + + - Using [`uvicorn`](https://www.uvicorn.org/) + + ```bash + uvicorn main:app --reload + ``` + +- Server at `http://127.0.0.1:8000/` + +- Documentation + + - Swagger UI at `http://127.0.0.1:8000/docs` + + - Redoc at `http://127.0.0.1:8000/docs` diff --git a/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/app/main.py b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/app/main.py new file mode 100644 index 0000000..f944934 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/app/main.py @@ -0,0 +1,14 @@ +from fastapi import FastAPI +from fastapi.exceptions import RequestValidationError +from fastapi.responses import PlainTextResponse + +app = FastAPI() + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request, exc:RequestValidationError): + return PlainTextResponse(str(exc), status_code=400) + +@app.get("/items/{item_id}") +async def read_item(item_id: int): + return item_id + diff --git a/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/requirements.txt b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/requirements.txt new file mode 100644 index 0000000..d068479 --- /dev/null +++ b/Geekyshows/Section-9.Handling Errors/3.Override default exception handlers/ch34/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.7.0 +anyio==4.9.0 +certifi==2025.4.26 +click==8.2.0 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.5 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.3 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1