Skip to content

Commit 00238d6

Browse files
authored
Merge pull request #524 from realpython/crud-operations
Add materials for the CRUD tutorial
2 parents e3c2cc0 + 58c1d0d commit 00238d6

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

crud-operations/README.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# What Are CRUD Operations?
2+
3+
This repository contains code related to the Real Python tutorial on [CRUD operations](https://realpython.com/crud-operations/).
4+
5+
## Setup
6+
7+
You should first create a virtual environment:
8+
9+
```console
10+
$ python -m venv venv
11+
$ source venv/bin/activate
12+
```
13+
14+
Install the pinned dependencies from `requirements.txt`:
15+
16+
```console
17+
(venv) $ python -m pip install -r requirements.txt
18+
```
19+
20+
Then you can execute `crud_sql_alchemy.py` to create a new database:
21+
22+
```sh
23+
(venv) $ python crud_sql_alchemy.py
24+
```
25+
26+
This will create a `birds.db` database that you can use to try out CRUD operations.
27+
28+
## Examples
29+
30+
Start a new [Python REPL](https://realpython.com/python-repl/) to perform CRUD operations with raw SQL:
31+
32+
```pycon
33+
>>> from crud_sql import connect_to_db
34+
>>> connection = connect_to_db("birds.db")
35+
>>> create_birds = """
36+
... INSERT INTO
37+
... bird (name)
38+
... VALUES
39+
... ('Hummingbird'),
40+
... ('Sugar Glider');
41+
... """
42+
>>> connection.execute(create_birds)
43+
<sqlite3.Cursor object at 0x105027bc0>
44+
45+
>>> connection.commit()
46+
>>> connection.close()
47+
```
48+
49+
You can also use SQL Alchemy to interact with the database:
50+
51+
```pycon
52+
>>> from crud_sql_alchemy import Session, Bird, init_db
53+
>>> init_db()
54+
>>> session = Session()
55+
>>> new_bird = Bird(name="Test Bird")
56+
>>> session.add(new_bird)
57+
>>> session.commit()
58+
```
59+
60+
If you're curious to explore CRUD operations with a REST API, then you can run this command in the terminal:
61+
62+
```console
63+
(venv) $ uvicorn crud_fastapi:app
64+
```
65+
66+
Once the server is running, you can test out the Rest API endpoints by visiting `http://127.0.0.1:8000/docs` in your browser.
67+
68+
## Author
69+
70+
- **Philipp Acsany**, E-mail: [[email protected]]([email protected])
71+
72+
## License
73+
74+
Distributed under the MIT license. See [`LICENSE`](../LICENSE) for more information.

crud-operations/crud_fastapi.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from fastapi import FastAPI, HTTPException, Depends
2+
from sqlalchemy import select
3+
from sqlalchemy.orm import Session
4+
from pydantic import BaseModel, ConfigDict
5+
6+
from crud_sql_alchemy import Bird, init_db
7+
from crud_sql_alchemy import Session as SessionLocal
8+
9+
app = FastAPI()
10+
init_db()
11+
12+
13+
class BirdCreate(BaseModel):
14+
name: str
15+
16+
17+
class BirdUpdate(BaseModel):
18+
name: str
19+
20+
21+
class BirdResponse(BaseModel):
22+
model_config = ConfigDict(from_attributes=True)
23+
24+
id: int
25+
name: str
26+
27+
28+
def get_db():
29+
db = SessionLocal()
30+
try:
31+
yield db
32+
finally:
33+
db.close()
34+
35+
36+
@app.post("/birds/", response_model=BirdResponse)
37+
def create_bird(bird: BirdCreate, db: Session = Depends(get_db)):
38+
new_bird = Bird(name=bird.name)
39+
db.add(new_bird)
40+
db.commit()
41+
db.refresh(new_bird)
42+
return new_bird
43+
44+
45+
@app.get("/birds/", response_model=list[BirdResponse])
46+
def read_birds(db: Session = Depends(get_db)):
47+
birds = db.execute(select(Bird)).scalars().all()
48+
return birds
49+
50+
51+
@app.get("/birds/{bird_id}", response_model=BirdResponse)
52+
def read_bird(bird_id: int, db: Session = Depends(get_db)):
53+
query = select(Bird).where(Bird.id == bird_id)
54+
found_bird = db.execute(query).scalar_one()
55+
if found_bird is None:
56+
raise HTTPException(status_code=404, detail="Bird not found")
57+
return found_bird
58+
59+
60+
@app.put("/birds/{bird_id}", response_model=BirdResponse)
61+
def update_bird(bird_id: int, bird: BirdUpdate, db: Session = Depends(get_db)):
62+
query = select(Bird).where(Bird.id == bird_id)
63+
found_bird = db.execute(query).scalar_one()
64+
if found_bird is None:
65+
raise HTTPException(status_code=404, detail="Bird not found")
66+
found_bird.name = bird.name
67+
db.commit()
68+
db.refresh(found_bird)
69+
return found_bird
70+
71+
72+
@app.delete("/birds/{bird_id}", response_model=dict)
73+
def delete_bird(bird_id: int, db: Session = Depends(get_db)):
74+
query = select(Bird).where(Bird.id == bird_id)
75+
found_bird = db.execute(query).scalar_one()
76+
if found_bird is None:
77+
raise HTTPException(status_code=404, detail="Bird not found")
78+
db.delete(found_bird)
79+
db.commit()
80+
return {"message": "Bird deleted successfully"}

crud-operations/crud_sql.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sqlite3
2+
3+
4+
def connect_to_db(db_path):
5+
return sqlite3.connect(db_path)
6+
7+
8+
if __name__ == "__main__":
9+
with connect_to_db("birds.db") as connection:
10+
connection.execute(
11+
"""
12+
CREATE TABLE IF NOT EXISTS bird (
13+
id INTEGER PRIMARY KEY AUTOINCREMENT,
14+
name TEXT NOT NULL
15+
);
16+
"""
17+
)

crud-operations/crud_sql_alchemy.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from sqlalchemy import create_engine, Column, Integer, String
2+
from sqlalchemy.orm import DeclarativeBase, sessionmaker
3+
4+
5+
class Base(DeclarativeBase):
6+
pass
7+
8+
9+
class Bird(Base):
10+
__tablename__ = "bird"
11+
id = Column(Integer, primary_key=True)
12+
name = Column(String)
13+
14+
def __repr__(self):
15+
return f"Bird(id={self.id}, name={self.name!r})"
16+
17+
18+
engine = create_engine("sqlite:///birds.db")
19+
Session = sessionmaker(bind=engine)
20+
21+
22+
def init_db():
23+
Base.metadata.create_all(engine)

crud-operations/requirements.txt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
annotated-types==0.6.0
2+
anyio==4.3.0
3+
click==8.1.7
4+
fastapi==0.110.1
5+
h11==0.14.0
6+
httptools==0.6.1
7+
idna==3.7
8+
pydantic==2.6.4
9+
pydantic_core==2.16.3
10+
python-dotenv==1.0.1
11+
PyYAML==6.0.1
12+
sniffio==1.3.1
13+
SQLAlchemy==2.0.29
14+
starlette==0.37.2
15+
typing_extensions==4.10.0
16+
uvicorn==0.29.0
17+
uvloop==0.19.0
18+
watchfiles==0.21.0
19+
websockets==12.0

0 commit comments

Comments
 (0)