Skip to content

Commit eed33d2

Browse files
authored
♻️ Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* ♻️ Refactor backend, update DB session handling * ✨ Add mypy config and plugins * ➕ Use Python-jose instead of PyJWT as it has some extra functionalities and features * ✨ Add/update scripts for test, lint, format * 🔧 Update lint and format configs * 🎨 Update import format, comments, and types * 🎨 Add types to config * ✨ Add types for all the code, and small fixes * 🎨 Use global imports to simplify exploring with Jupyter * ♻️ Import schemas and models, instead of each class * 🚚 Rename db_session to db for simplicity * 📌 Update dependencies installation for testing
1 parent 4b80bdf commit eed33d2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+545
-443
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[flake8]
2+
max-line-length = 88
3+
exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.mypy_cache
2+
.coverage
3+
htmlcov
Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,99 @@
1-
from typing import List
1+
from typing import Any, List
22

33
from fastapi import APIRouter, Depends, HTTPException
44
from sqlalchemy.orm import Session
55

6-
from app import crud
7-
from app.api.utils.db import get_db
8-
from app.api.utils.security import get_current_active_user
9-
from app.models.user import User as DBUser
10-
from app.schemas.item import Item, ItemCreate, ItemUpdate
6+
from app import crud, models, schemas
7+
from app.api import deps
118

129
router = APIRouter()
1310

1411

15-
@router.get("/", response_model=List[Item])
12+
@router.get("/", response_model=List[schemas.Item])
1613
def read_items(
17-
db: Session = Depends(get_db),
14+
db: Session = Depends(deps.get_db),
1815
skip: int = 0,
1916
limit: int = 100,
20-
current_user: DBUser = Depends(get_current_active_user),
21-
):
17+
current_user: models.User = Depends(deps.get_current_active_user),
18+
) -> Any:
2219
"""
2320
Retrieve items.
2421
"""
2522
if crud.user.is_superuser(current_user):
2623
items = crud.item.get_multi(db, skip=skip, limit=limit)
2724
else:
2825
items = crud.item.get_multi_by_owner(
29-
db_session=db, owner_id=current_user.id, skip=skip, limit=limit
26+
db=db, owner_id=current_user.id, skip=skip, limit=limit
3027
)
3128
return items
3229

3330

34-
@router.post("/", response_model=Item)
31+
@router.post("/", response_model=schemas.Item)
3532
def create_item(
3633
*,
37-
db: Session = Depends(get_db),
38-
item_in: ItemCreate,
39-
current_user: DBUser = Depends(get_current_active_user),
40-
):
34+
db: Session = Depends(deps.get_db),
35+
item_in: schemas.ItemCreate,
36+
current_user: models.User = Depends(deps.get_current_active_user),
37+
) -> Any:
4138
"""
4239
Create new item.
4340
"""
44-
item = crud.item.create_with_owner(
45-
db_session=db, obj_in=item_in, owner_id=current_user.id
46-
)
41+
item = crud.item.create_with_owner(db=db, obj_in=item_in, owner_id=current_user.id)
4742
return item
4843

4944

50-
@router.put("/{id}", response_model=Item)
45+
@router.put("/{id}", response_model=schemas.Item)
5146
def update_item(
5247
*,
53-
db: Session = Depends(get_db),
48+
db: Session = Depends(deps.get_db),
5449
id: int,
55-
item_in: ItemUpdate,
56-
current_user: DBUser = Depends(get_current_active_user),
57-
):
50+
item_in: schemas.ItemUpdate,
51+
current_user: models.User = Depends(deps.get_current_active_user),
52+
) -> Any:
5853
"""
5954
Update an item.
6055
"""
61-
item = crud.item.get(db_session=db, id=id)
56+
item = crud.item.get(db=db, id=id)
6257
if not item:
6358
raise HTTPException(status_code=404, detail="Item not found")
6459
if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
6560
raise HTTPException(status_code=400, detail="Not enough permissions")
66-
item = crud.item.update(db_session=db, db_obj=item, obj_in=item_in)
61+
item = crud.item.update(db=db, db_obj=item, obj_in=item_in)
6762
return item
6863

6964

70-
@router.get("/{id}", response_model=Item)
65+
@router.get("/{id}", response_model=schemas.Item)
7166
def read_item(
7267
*,
73-
db: Session = Depends(get_db),
68+
db: Session = Depends(deps.get_db),
7469
id: int,
75-
current_user: DBUser = Depends(get_current_active_user),
76-
):
70+
current_user: models.User = Depends(deps.get_current_active_user),
71+
) -> Any:
7772
"""
7873
Get item by ID.
7974
"""
80-
item = crud.item.get(db_session=db, id=id)
75+
item = crud.item.get(db=db, id=id)
8176
if not item:
8277
raise HTTPException(status_code=404, detail="Item not found")
8378
if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
8479
raise HTTPException(status_code=400, detail="Not enough permissions")
8580
return item
8681

8782

88-
@router.delete("/{id}", response_model=Item)
83+
@router.delete("/{id}", response_model=schemas.Item)
8984
def delete_item(
9085
*,
91-
db: Session = Depends(get_db),
86+
db: Session = Depends(deps.get_db),
9287
id: int,
93-
current_user: DBUser = Depends(get_current_active_user),
94-
):
88+
current_user: models.User = Depends(deps.get_current_active_user),
89+
) -> Any:
9590
"""
9691
Delete an item.
9792
"""
98-
item = crud.item.get(db_session=db, id=id)
93+
item = crud.item.get(db=db, id=id)
9994
if not item:
10095
raise HTTPException(status_code=404, detail="Item not found")
10196
if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
10297
raise HTTPException(status_code=400, detail="Not enough permissions")
103-
item = crud.item.remove(db_session=db, id=id)
98+
item = crud.item.remove(db=db, id=id)
10499
return item

{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
from datetime import timedelta
2+
from typing import Any
23

34
from fastapi import APIRouter, Body, Depends, HTTPException
45
from fastapi.security import OAuth2PasswordRequestForm
56
from sqlalchemy.orm import Session
67

7-
from app import crud
8-
from app.api.utils.db import get_db
9-
from app.api.utils.security import get_current_user
8+
from app import crud, models, schemas
9+
from app.api import deps
10+
from app.core import security
1011
from app.core.config import settings
11-
from app.core.jwt import create_access_token
1212
from app.core.security import get_password_hash
13-
from app.models.user import User as DBUser
14-
from app.schemas.msg import Msg
15-
from app.schemas.token import Token
16-
from app.schemas.user import User
1713
from app.utils import (
1814
generate_password_reset_token,
1915
send_reset_password_email,
@@ -23,10 +19,10 @@
2319
router = APIRouter()
2420

2521

26-
@router.post("/login/access-token", response_model=Token)
22+
@router.post("/login/access-token", response_model=schemas.Token)
2723
def login_access_token(
28-
db: Session = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()
29-
):
24+
db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
25+
) -> Any:
3026
"""
3127
OAuth2 compatible token login, get an access token for future requests
3228
"""
@@ -39,23 +35,23 @@ def login_access_token(
3935
raise HTTPException(status_code=400, detail="Inactive user")
4036
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
4137
return {
42-
"access_token": create_access_token(
43-
data={"user_id": user.id}, expires_delta=access_token_expires
38+
"access_token": security.create_access_token(
39+
user.id, expires_delta=access_token_expires
4440
),
4541
"token_type": "bearer",
4642
}
4743

4844

49-
@router.post("/login/test-token", response_model=User)
50-
def test_token(current_user: DBUser = Depends(get_current_user)):
45+
@router.post("/login/test-token", response_model=schemas.User)
46+
def test_token(current_user: models.User = Depends(deps.get_current_user)) -> Any:
5147
"""
5248
Test access token
5349
"""
5450
return current_user
5551

5652

57-
@router.post("/password-recovery/{email}", response_model=Msg)
58-
def recover_password(email: str, db: Session = Depends(get_db)):
53+
@router.post("/password-recovery/{email}", response_model=schemas.Msg)
54+
def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
5955
"""
6056
Password Recovery
6157
"""
@@ -73,10 +69,12 @@ def recover_password(email: str, db: Session = Depends(get_db)):
7369
return {"msg": "Password recovery email sent"}
7470

7571

76-
@router.post("/reset-password/", response_model=Msg)
72+
@router.post("/reset-password/", response_model=schemas.Msg)
7773
def reset_password(
78-
token: str = Body(...), new_password: str = Body(...), db: Session = Depends(get_db)
79-
):
74+
token: str = Body(...),
75+
new_password: str = Body(...),
76+
db: Session = Depends(deps.get_db),
77+
) -> Any:
8078
"""
8179
Reset password
8280
"""

{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,39 @@
1-
from typing import List
1+
from typing import Any, List
22

33
from fastapi import APIRouter, Body, Depends, HTTPException
44
from fastapi.encoders import jsonable_encoder
55
from pydantic.networks import EmailStr
66
from sqlalchemy.orm import Session
77

8-
from app import crud
9-
from app.api.utils.db import get_db
10-
from app.api.utils.security import get_current_active_superuser, get_current_active_user
8+
from app import crud, models, schemas
9+
from app.api import deps
1110
from app.core.config import settings
12-
from app.models.user import User as DBUser
13-
from app.schemas.user import User, UserCreate, UserUpdate
1411
from app.utils import send_new_account_email
1512

1613
router = APIRouter()
1714

1815

19-
@router.get("/", response_model=List[User])
16+
@router.get("/", response_model=List[schemas.User])
2017
def read_users(
21-
db: Session = Depends(get_db),
18+
db: Session = Depends(deps.get_db),
2219
skip: int = 0,
2320
limit: int = 100,
24-
current_user: DBUser = Depends(get_current_active_superuser),
25-
):
21+
current_user: models.User = Depends(deps.get_current_active_superuser),
22+
) -> Any:
2623
"""
2724
Retrieve users.
2825
"""
2926
users = crud.user.get_multi(db, skip=skip, limit=limit)
3027
return users
3128

3229

33-
@router.post("/", response_model=User)
30+
@router.post("/", response_model=schemas.User)
3431
def create_user(
3532
*,
36-
db: Session = Depends(get_db),
37-
user_in: UserCreate,
38-
current_user: DBUser = Depends(get_current_active_superuser),
39-
):
33+
db: Session = Depends(deps.get_db),
34+
user_in: schemas.UserCreate,
35+
current_user: models.User = Depends(deps.get_current_active_superuser),
36+
) -> Any:
4037
"""
4138
Create new user.
4239
"""
@@ -54,20 +51,20 @@ def create_user(
5451
return user
5552

5653

57-
@router.put("/me", response_model=User)
54+
@router.put("/me", response_model=schemas.User)
5855
def update_user_me(
5956
*,
60-
db: Session = Depends(get_db),
57+
db: Session = Depends(deps.get_db),
6158
password: str = Body(None),
6259
full_name: str = Body(None),
6360
email: EmailStr = Body(None),
64-
current_user: DBUser = Depends(get_current_active_user),
65-
):
61+
current_user: models.User = Depends(deps.get_current_active_user),
62+
) -> Any:
6663
"""
6764
Update own user.
6865
"""
6966
current_user_data = jsonable_encoder(current_user)
70-
user_in = UserUpdate(**current_user_data)
67+
user_in = schemas.UserUpdate(**current_user_data)
7168
if password is not None:
7269
user_in.password = password
7370
if full_name is not None:
@@ -78,25 +75,25 @@ def update_user_me(
7875
return user
7976

8077

81-
@router.get("/me", response_model=User)
78+
@router.get("/me", response_model=schemas.User)
8279
def read_user_me(
83-
db: Session = Depends(get_db),
84-
current_user: DBUser = Depends(get_current_active_user),
85-
):
80+
db: Session = Depends(deps.get_db),
81+
current_user: models.User = Depends(deps.get_current_active_user),
82+
) -> Any:
8683
"""
8784
Get current user.
8885
"""
8986
return current_user
9087

9188

92-
@router.post("/open", response_model=User)
89+
@router.post("/open", response_model=schemas.User)
9390
def create_user_open(
9491
*,
95-
db: Session = Depends(get_db),
92+
db: Session = Depends(deps.get_db),
9693
password: str = Body(...),
9794
email: EmailStr = Body(...),
9895
full_name: str = Body(None),
99-
):
96+
) -> Any:
10097
"""
10198
Create new user without the need to be logged in.
10299
"""
@@ -111,17 +108,17 @@ def create_user_open(
111108
status_code=400,
112109
detail="The user with this username already exists in the system",
113110
)
114-
user_in = UserCreate(password=password, email=email, full_name=full_name)
111+
user_in = schemas.UserCreate(password=password, email=email, full_name=full_name)
115112
user = crud.user.create(db, obj_in=user_in)
116113
return user
117114

118115

119-
@router.get("/{user_id}", response_model=User)
116+
@router.get("/{user_id}", response_model=schemas.User)
120117
def read_user_by_id(
121118
user_id: int,
122-
current_user: DBUser = Depends(get_current_active_user),
123-
db: Session = Depends(get_db),
124-
):
119+
current_user: models.User = Depends(deps.get_current_active_user),
120+
db: Session = Depends(deps.get_db),
121+
) -> Any:
125122
"""
126123
Get a specific user by id.
127124
"""
@@ -135,14 +132,14 @@ def read_user_by_id(
135132
return user
136133

137134

138-
@router.put("/{user_id}", response_model=User)
135+
@router.put("/{user_id}", response_model=schemas.User)
139136
def update_user(
140137
*,
141-
db: Session = Depends(get_db),
138+
db: Session = Depends(deps.get_db),
142139
user_id: int,
143-
user_in: UserUpdate,
144-
current_user: DBUser = Depends(get_current_active_superuser),
145-
):
140+
user_in: schemas.UserUpdate,
141+
current_user: models.User = Depends(deps.get_current_active_superuser),
142+
) -> Any:
146143
"""
147144
Update a user.
148145
"""

0 commit comments

Comments
 (0)