Skip to content

Commit 2458ceb

Browse files
authored
Merge pull request #94 from sqlitecloud/feat/fastapi-sqlalchemy-quickstart
Added SQLAlchemy Quickstart guide
2 parents fbc0a94 + 9036a6d commit 2458ceb

2 files changed

Lines changed: 197 additions & 0 deletions

File tree

sqlite-cloud/_nav.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const sidebarNav: SidebarNavStruct = [
1717
{ title: "Next.js", filePath: "quick-start-next", type: "inner", level: 1 },
1818
{ title: "Django", filePath: "quick-start-django", type: "inner", level: 1 },
1919
{ title: "Flask", filePath: "quick-start-flask", type: "inner", level: 1 },
20+
{ title: "SQLAlchemy", filePath: "quick-start-sqlalchemy-orm", type: "inner", level: 1 },
2021
{ title: "Streamlit", filePath: "quick-start-streamlit", type: "inner", level: 1 },
2122
{ title: "PHP / Laravel", filePath: "quick-start-php-laravel", type: "inner", level: 1 },
2223
{ title: "Gin", filePath: "quick-start-gin", type: "inner", level: 1 },
@@ -162,6 +163,7 @@ const sidebarNav: SidebarNavStruct = [
162163
{ title: 'Introduction', type: "inner", filePath: "sdk-python-introduction", level: 1 },
163164
{ title: "Django", ref: "/docs/quick-start-django", type: "inner", level: 1 },
164165
{ title: "Flask", ref: "/docs/quick-start-flask", type: "inner", level: 1 },
166+
{ title: "SQLAlchemy", ref: "/docs/quick-start-sqlalchemy-orm", type: "inner", level: 1 },
165167

166168
{ title: "Go", type: "inner", level: 0 },
167169
{ title: 'Introduction', type: "inner", filePath: "sdk-go-introduction", level: 1 },
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
title: SQLAlchemy ORM Quick Start Guide
3+
description: Get started with SQLite Cloud using SQLAlchemy ORM in FastAPI.
4+
category: getting-started
5+
status: publish
6+
slug: quick-start-sqlalchemy-orm
7+
---
8+
9+
In this quickstart, we will show you how to get started with SQLite Cloud and SQLAlchemy by building a FastAPI backend that connects to and reads from a SQLite Cloud database.
10+
11+
---
12+
13+
1. **Set up a SQLite Cloud account**
14+
- If you haven't already, [sign up for a SQLite Cloud account](https://sqlitecloud.io/register) and create a new project.
15+
- In this guide, we will use the sample datasets that come pre-loaded with SQLite Cloud.
16+
17+
2. **Create a new Python project**
18+
- You should have the latest Python version (3) installed locally.
19+
20+
```bash
21+
mkdir sqlalchemy-quickstart
22+
cd sqlalchemy-quickstart
23+
24+
# open the project in VSCode
25+
# code .
26+
27+
python3 -m venv .venv
28+
. .venv/bin/activate
29+
```
30+
31+
3. **Install dependencies**
32+
- Run this command from your current directory:
33+
34+
```bash
35+
pip install "fastapi[standard]" sqlalchemy sqlalchemy-sqlitecloud
36+
```
37+
38+
- Do NOT remove the quotes around the FastAPI package.
39+
- `sqlalchemy-sqlitecloud` includes `sqlitecloud`, so no need to install the latter separately.
40+
- Update `.venv/lib/python{version}/site-packages/sqlitecloud/__init__.py`:
41+
42+
```py
43+
from .dbapi2 import *
44+
45+
# from .dbapi2 import (
46+
# PARSE_COLNAMES,
47+
# ...
48+
# )
49+
```
50+
51+
4. **App setup**
52+
- From your current directory, create a sub-directory `fastapi_sqlc_app` with an empty `__init__.py` file to indicate the new sub-dir is a package.
53+
- NOTE: Create the remaining project files in this sub-dir as well.
54+
55+
```bash
56+
mkdir fastapi_sqlc_app
57+
cd fastapi_sqlc_app
58+
touch __init__.py
59+
```
60+
61+
- Create a new file `database.py` and copy in the following code.
62+
- In your SQLite Cloud account dashboard, click on `Show connection strings`, copy the Connection String, and replace `<your-connection-string>` below. Modify your string to include the name of the DB we'll query: `sqlitecloud://{hostname}:8860/chinook.sqlite?apikey={apikey}`.
63+
64+
```py
65+
from sqlalchemy import create_engine
66+
from sqlalchemy.orm import sessionmaker
67+
from sqlalchemy.ext.declarative import declarative_base
68+
69+
engine = create_engine('<your-connection-string>')
70+
71+
SessionLocal = sessionmaker(bind=engine)
72+
73+
Base = declarative_base()
74+
```
75+
76+
- Create a new file `models.py` and copy in the following code defining 2 SQLAlchemy ORM "models", or classes, to interact with the DB.
77+
- `__tablename__` is the name of a model's corresponding DB table.
78+
- Most class attributes/ table `Column`s below are passed a class type as the first argument. NOTE: The `Album` class' `id` attribute passes `AlbumId` as the first arg.
79+
- We use the `relationship` function to create an explicit, bidirectional link between the 2 models. Let's say:
80+
- We have an instance of the `Artist` class called `taylor_swift`. Accessing the attribute `taylor_swift.albums` would return:
81+
- a list of `Album` SQLAlchemy models from the `albums` DB table, whose foreign key `ArtistId` points to the `taylor_swift` record in the `artists` table.
82+
- Or simply put, a list of Taylor Swift's albums.
83+
- We have an instance of the `Album` class called `fearless`. Accessing the attribute `fearless.artist` would return:
84+
- an `Artist` SQLAlchemy model from the `artists` DB table, again using the foreign key `ArtistId` to get the right record.
85+
- Or simply put, the creator of the album Fearless (also Taylor Swift).
86+
87+
```py
88+
from .database import Base
89+
90+
from sqlalchemy import Column, ForeignKey, Integer, String
91+
from sqlalchemy.orm import relationship
92+
93+
class Artist(Base):
94+
__tablename__ = "artists"
95+
96+
ArtistId = Column(Integer, primary_key=True)
97+
Name = Column(String)
98+
99+
albums = relationship("Album", back_populates="artist")
100+
101+
class Album(Base):
102+
__tablename__ = "albums"
103+
104+
id = Column("AlbumId", Integer, primary_key=True)
105+
Title = Column(String)
106+
ArtistId = Column(Integer, ForeignKey('artists.ArtistId'))
107+
108+
artist = relationship("Artist", back_populates="albums")
109+
```
110+
111+
- Create a new file `schemas.py` and copy in the following code defining 2 Pydantic models, or "schemas", to validate the shapes of the response data.
112+
- NOTE: The `Album` Pydantic model checks for `ArtistName` rather than `ArtistId` defined in the `Album` SQLAlchemy model in `models.py`.
113+
- By default, SQLAlchemy "lazy loads", i.e. it only gets relationship data from the DB when we access the model's attribute containing that data.`orm_mode = True` enables the Pydantic model to read a returned ORM (in this case, SQLAlchemy) model and include relationship data.
114+
115+
```py
116+
from pydantic import BaseModel
117+
118+
class Album(BaseModel):
119+
id: int
120+
Title: str
121+
ArtistName: str
122+
123+
class Config:
124+
orm_mode = True
125+
126+
class Artist(BaseModel):
127+
ArtistId: int
128+
Name: str
129+
albums: list[Album] = []
130+
131+
class Config:
132+
orm_mode = True
133+
```
134+
135+
- Create a new file `read.py` and copy in the following code creating a reusable utility function to read album data.
136+
137+
```py
138+
from . import models
139+
140+
from sqlalchemy.orm import Session
141+
142+
def get_albums(db: Session, skip: int = 0, num: int = 20):
143+
return db.query(models.Album.id, models.Album.Title, models.Artist.Name.label('ArtistName')).join(models.Artist).offset(skip).limit(num).all()
144+
```
145+
146+
- Create a new file `main.py` and copy in the following code.
147+
- The `get_db` function handles creating and closing a new `SessionLocal` instance, or DB connection/ session, for every request.
148+
- NOTE: The function below returns a list of SQLAlchemy `Album` models. However, only the data declared in the Pydantic schemas will be returned to the client.
149+
150+
```py
151+
from .database import SessionLocal
152+
from . import read, schemas
153+
154+
from fastapi import FastAPI, Depends
155+
from sqlalchemy.orm import Session
156+
157+
app = FastAPI()
158+
159+
def get_db():
160+
db = SessionLocal()
161+
try:
162+
yield db
163+
finally:
164+
db.close()
165+
166+
@app.get("/albums/", response_model=list[schemas.Album])
167+
def read_albums(skip: int = 0, num: int = 20, db: Session = Depends(get_db)):
168+
albums = read.get_albums(db, skip=skip, num=num)
169+
return albums
170+
```
171+
172+
5. **Run your FastAPI app**
173+
- From your `sqlalchemy-quickstart` directory, run the following command:
174+
175+
```bash
176+
uvicorn fastapi_sqlc_app.main:app --reload
177+
```
178+
179+
- Visit `http://127.0.0.1:8000/albums/` to see your app data.
180+
181+
6. **Troubleshooting**
182+
183+
- If you encounter the following error, restart your IDE and re-run your app.
184+
185+
```bash
186+
AttributeError: module 'sqlitecloud.dbapi2' has no attribute 'sqlite_version_info'`
187+
```
188+
189+
7. **References**
190+
191+
If you're new to FastAPI and/or want to learn more about how to work with ORMs in FastAPI, we referenced FastAPI's [SQL Databases Tutorial](https://fastapi.tiangolo.com/tutorial/sql-databases/) extensively when writing this Quickstart.
192+
193+
If you're new to SQLAlchemy, refer to [their latest docs](https://docs.sqlalchemy.org/en/20/).
194+
195+
And that's it! You've successfully built a FastAPI app that uses SQLAlchemy ORM to read data from a SQLite Cloud database.

0 commit comments

Comments
 (0)