Convert to SQLModel + adding matchday

This commit is contained in:
Simon 2022-09-19 10:45:08 +02:00
parent bf3bd68e52
commit 3b6ba0a2a4
37 changed files with 705 additions and 291 deletions

View File

@ -4,9 +4,13 @@ from app.api.api_v1.endpoints import users
from app.api.api_v1.endpoints import login
from app.api.api_v1.endpoints import player
from app.api.api_v1.endpoints import team
from app.api.api_v1.endpoints import match
from app.api.api_v1.endpoints import matchday
api_router = APIRouter()
api_router.include_router(login.router, tags=["login"])
api_router.include_router(users.router, prefix="/users", tags=["users"])
api_router.include_router(player.router, prefix="/player", tags=["player"])
api_router.include_router(team.router, prefix="/team", tags=["team"])
api_router.include_router(matchday.router, prefix="/matchday", tags=["matchday"])
#api_router.include_router(match.router, prefix="/match", tags=["match"])

View File

@ -5,11 +5,14 @@ from fastapi import APIRouter, Body, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app import crud
from app.api import deps
from app.core import security
from app.core.config import settings
from app.core.security import get_password_hash
from app.models.msg import Msg
from app.models.token import Token
from app.models.user import User
from app.utils import (
generate_password_reset_token,
send_reset_password_email,
@ -19,7 +22,7 @@ from app.utils import (
router = APIRouter()
@router.post("/login/access-token", response_model=schemas.Token)
@router.post("/login/access-token", response_model=Token)
def login_access_token(
db: Session = Depends(deps.get_db),
form_data: OAuth2PasswordRequestForm = Depends(),
@ -47,9 +50,9 @@ def login_access_token(
}
@router.post("/login/test-token", response_model=schemas.User)
@router.post("/login/test-token", response_model=User)
def test_token(
current_user: models.User = Depends(deps.get_current_user),
current_user: User = Depends(deps.get_current_user),
) -> Any:
"""
Test access token
@ -57,7 +60,7 @@ def test_token(
return current_user
@router.post("/password-recovery/{email}", response_model=schemas.Msg)
@router.post("/password-recovery/{email}", response_model=Msg)
def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
"""
Password Recovery
@ -76,7 +79,7 @@ def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
return {"msg": "Password recovery email sent"}
@router.post("/reset-password/", response_model=schemas.Msg)
@router.post("/reset-password/", response_model=Msg)
def reset_password(
token: str = Body(...),
new_password: str = Body(...),

View File

@ -0,0 +1,96 @@
from typing import Any, List
from fastapi import APIRouter, Body, Depends, HTTPException
from fastapi.encoders import jsonable_encoder
from pydantic.networks import EmailStr
from sqlalchemy.orm import Session
from app import crud
from app.api import deps
from app.models.player import Player, PlayerCreate, PlayerUpdate
from app.models.user import User
router = APIRouter()
@router.get("/{id}", response_model=Player)
def read_player(
*,
db: Session = Depends(deps.get_db),
firstname: str,
lastname: str,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get player by firstname and lastname.
"""
player = crud.player.get_player_by_name(
db=db, firstname=firstname, lastname=lastname
)
if not player:
raise HTTPException(status_code=404, detail="player not found")
if not crud.user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not enough permissions")
return player
@router.post("/", response_model=List[Player])
def create_player(
*,
db: Session = Depends(deps.get_db),
players_in: list[PlayerCreate],
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create new user.
"""
player_out = []
for player_in in players_in:
player = crud.player.get_player_by_name(
db, firstname=player_in.firstname, lastname=player_in.lastname
)
if player:
raise HTTPException(
status_code=400,
detail=f"The user with this username already exists in the "
f"system. Player is {player}",
)
player = crud.player.create(db, obj_in=player_in)
player_out.append(player)
return player_out
@router.get("/", response_model=List[Player])
def get_players(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Retrieve all players.
"""
player = crud.player.get_multi(db, skip=skip, limit=limit)
return player
@router.post("/{id}", response_model=Player)
def update_player(
*,
db: Session = Depends(deps.get_db),
id: int,
player_in: PlayerUpdate,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update player.
"""
player = crud.player.get(db=db, id=id)
if not player:
raise HTTPException(status_code=404, detail="Player not found")
player = crud.player.update(db=db, db_obj=player, obj_in=player_in)
return player

View File

@ -0,0 +1,114 @@
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app import crud
from app.api import deps
from app.models.matchday import Matchday, MatchdayCreate, MatchdayUpdate, \
MatchdayWithPlayers
from app.models.user import User
router = APIRouter()
@router.get("/{matchday_id}", response_model=MatchdayWithPlayers)
def read_matchday(
*,
db: Session = Depends(deps.get_db),
matchday_id: int,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get matchday by firstname and lastname.
"""
matchday = crud.matchday.get(
db=db, id=matchday_id
)
if not matchday:
raise HTTPException(status_code=404, detail="matchday not found")
if not crud.user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not enough permissions")
return matchday
@router.post("/", response_model=Matchday)
def create_matchday(
*,
db: Session = Depends(deps.get_db),
matchday_in: MatchdayCreate,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create new user.
"""
matchday = crud.matchday.get_unique_day(db, obj_in=matchday_in)
if matchday:
raise HTTPException(
status_code=400,
detail=f"The matchday with this day already exists in the "
f"system. Matchday is {matchday}",
)
matchday = crud.matchday.create(db, obj_in=matchday_in)
return matchday
@router.get("/", response_model=List[MatchdayWithPlayers])
def get_matchdays(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Retrieve all matchdays.
"""
matchday = crud.matchday.get_multi(db, skip=skip, limit=limit)
return matchday
@router.post("/{matchday_id}", response_model=Matchday)
def update_matchday(
*,
db: Session = Depends(deps.get_db),
matchday_id: int,
matchday_in: MatchdayUpdate,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update matchday.
"""
matchday = crud.matchday.get(db=db, id=matchday_id)
if not matchday:
raise HTTPException(status_code=404, detail="Matchday not found")
matchday = crud.matchday.update(db=db, db_obj=matchday, obj_in=matchday_in)
return matchday
@router.put("/players/{matchday_id}", response_model=Matchday)
def add_player_matchday(
*,
db: Session = Depends(deps.get_db),
player_id: int,
matchday_id: int,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Add player to matchday.
"""
if crud.player.get(db, id=player_id) is None:
raise HTTPException(status_code=404, detail="Player not found")
if crud.matchday.get(db=db, id=matchday_id) is None:
raise HTTPException(status_code=404, detail="Matchday not found")
if crud.matchday.is_player_in_matchday(
db=db, player_id=player_id, matchday_id=matchday_id
):
raise HTTPException(
status_code=404, detail="Player is already in team"
)
matchday = crud.matchday.add_player_in_matchday(db, matchday_id, player_id)
return matchday

View File

@ -1,31 +1,30 @@
from typing import Any, List
from fastapi import APIRouter, Body, Depends, HTTPException
from fastapi.encoders import jsonable_encoder
from pydantic.networks import EmailStr
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app import crud
from app.api import deps
from app.core.config import settings
from app.schemas.player import PlayerUpdate
from app.models.player import Player, PlayerCreate, PlayerUpdate, \
PlayerTeamsMatchdays
from app.models.user import User
router = APIRouter()
@router.get("/{id}", response_model=schemas.Player)
@router.get("/{player_id}", response_model=PlayerTeamsMatchdays)
def read_player(
*,
db: Session = Depends(deps.get_db),
firstname: str,
lastname: str,
current_user: models.User = Depends(deps.get_current_active_user),
player_id: int,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get player by firstname and lastname.
Get player by id.
"""
player = crud.player.get_player_by_name(
db=db, firstname=firstname, lastname=lastname
player = crud.player.get(
db=db, id=player_id
)
if not player:
raise HTTPException(status_code=404, detail="player not found")
@ -34,56 +33,60 @@ def read_player(
return player
@router.post("/", response_model=schemas.Player)
@router.post("/", response_model=List[Player])
def create_player(
*,
db: Session = Depends(deps.get_db),
player_in: schemas.PlayerCreate,
current_user: models.User = Depends(deps.get_current_active_superuser),
players_in: list[PlayerCreate],
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create new user.
"""
player = crud.player.get_player_by_name(
db, firstname=player_in.firstname, lastname=player_in.lastname
)
if player:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system.",
player_out = []
for player_in in players_in:
player = crud.player.get_player_by_name(
db, firstname=player_in.firstname, lastname=player_in.lastname
)
player = crud.player.create(db, obj_in=player_in)
if player:
raise HTTPException(
status_code=400,
detail=f"The user with this username already exists in the "
f"system. Player is {player}",
)
player = crud.player.create(db, obj_in=player_in)
player_out.append(player)
return player
return player_out
@router.get("/", response_model=List[schemas.Player])
@router.get("/", response_model=List[PlayerTeamsMatchdays])
def get_players(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Depends(deps.get_current_active_superuser),
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Retrieve all players.
"""
player = crud.player.get_players(db, skip=skip, limit=limit)
player = crud.player.get_multi(db, skip=skip, limit=limit)
return player
@router.post("/{id}", response_model=schemas.Player)
@router.post("/{player_id}", response_model=Player)
def update_player(
*,
db: Session = Depends(deps.get_db),
id: int,
player_id: int,
player_in: PlayerUpdate,
current_user: models.User = Depends(deps.get_current_active_user),
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update player.
"""
player = crud.player.get(db=db, id=id)
player = crud.player.get(db=db, id=player_id)
if not player:
raise HTTPException(status_code=404, detail="Player not found")

View File

@ -4,24 +4,25 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app import crud
from app.api import deps
from app.models.team import Team, TeamCreate, TeamWithPlayers
from app.models.user import User
router = APIRouter()
@router.get("/{id}", response_model=schemas.Team)
@router.get("/{team_id}", response_model=Team)
def get_team(
*,
db: Session = Depends(deps.get_db),
id: int,
current_user: models.User = Depends(deps.get_current_active_user),
team_id: int,
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get team by id.
"""
team = crud.team.get_team(db=db, team_id=id)
team = crud.team.get_team(db=db, team_id=team_id)
if not team:
raise HTTPException(status_code=404, detail="player not found")
if not crud.user.is_superuser(current_user):
@ -29,26 +30,26 @@ def get_team(
return team
@router.get("/", response_model=List[schemas.Team])
@router.get("/", response_model=List[TeamWithPlayers])
def get_teams(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Depends(deps.get_current_active_superuser),
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Retrieve teams.
"""
team = crud.team.get_teams(db, skip=skip, limit=limit)
team = crud.team.get_multi( db, skip=skip, limit=limit)
return team
@router.post("/", response_model=schemas.Team)
@router.post("/", response_model=Team)
def create_team(
*,
db: Session = Depends(deps.get_db),
team_in: schemas.TeamCreate,
current_user: models.User = Depends(deps.get_current_active_superuser),
team_in: TeamCreate,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create team.
@ -59,20 +60,20 @@ def create_team(
return team
@router.put("/players/{team_id}", response_model=schemas.Team)
@router.put("/players/{team_id}", response_model=Team)
def add_player_team(
*,
db: Session = Depends(deps.get_db),
player_id: int,
team_id: int,
current_user: models.User = Depends(deps.get_current_active_superuser),
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Add player to team.
"""
if crud.player.get_player(db, player_id=player_id) is None:
if crud.player.get(db, id=player_id) is None:
raise HTTPException(status_code=404, detail="Player not found")
if crud.team.get_team(db=db, team_id=team_id) is None:
if crud.team.get(db=db, id=team_id) is None:
raise HTTPException(status_code=404, detail="Team not found")
if crud.team.is_player_in_team(
db=db, player_id=player_id, team_id=team_id

View File

@ -5,20 +5,20 @@ from fastapi.encoders import jsonable_encoder
from pydantic.networks import EmailStr
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app import crud, models
from app.api import deps
from app.core.config import settings
from app.models.user import UserUpdate, User, UserCreate
router = APIRouter()
@router.get("/", response_model=List[schemas.User])
@router.get("/", response_model=List[User])
def read_users(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Depends(deps.get_current_active_superuser),
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Retrieve users.
@ -27,12 +27,12 @@ def read_users(
return users
@router.post("/", response_model=schemas.User)
@router.post("/", response_model=User)
def create_user(
*,
db: Session = Depends(deps.get_db),
user_in: schemas.UserCreate,
current_user: models.User = Depends(deps.get_current_active_superuser),
user_in: UserCreate,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create new user.
@ -48,20 +48,20 @@ def create_user(
return user
@router.put("/me", response_model=schemas.User)
@router.put("/me", response_model=User)
def update_user_me(
*,
db: Session = Depends(deps.get_db),
password: str = Body(None),
full_name: str = Body(None),
email: EmailStr = Body(None),
current_user: models.User = Depends(deps.get_current_active_user),
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update own user.
"""
current_user_data = jsonable_encoder(current_user)
user_in = schemas.UserUpdate(**current_user_data)
user_in = UserUpdate(**current_user_data)
if password is not None:
user_in.password = password
if full_name is not None:
@ -72,10 +72,10 @@ def update_user_me(
return user
@router.get("/me", response_model=schemas.User)
@router.get("/me", response_model=User)
def read_user_me(
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user),
current_user: User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get current user.
@ -83,7 +83,7 @@ def read_user_me(
return current_user
@router.post("/open", response_model=schemas.User)
@router.post("/open", response_model=User)
def create_user_open(
*,
db: Session = Depends(deps.get_db),
@ -105,17 +105,17 @@ def create_user_open(
status_code=400,
detail="The user with this username already exists in the system",
)
user_in = schemas.UserCreate(
user_in = UserCreate(
password=password, email=email, full_name=full_name
)
user = crud.user.create(db, obj_in=user_in)
return user
@router.get("/{user_id}", response_model=schemas.User)
@router.get("/{user_id}", response_model=User)
def read_user_by_id(
user_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
current_user: User = Depends(deps.get_current_active_user),
db: Session = Depends(deps.get_db),
) -> Any:
"""
@ -131,13 +131,13 @@ def read_user_by_id(
return user
@router.put("/{user_id}", response_model=schemas.User)
@router.put("/{user_id}", response_model=User)
def update_user(
*,
db: Session = Depends(deps.get_db),
user_id: int,
user_in: schemas.UserUpdate,
current_user: models.User = Depends(deps.get_current_active_superuser),
user_in: UserUpdate,
current_user: User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Update a user.

View File

@ -6,7 +6,9 @@ from jose import jwt
from pydantic import ValidationError
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app import crud
from app.models.token import TokenPayload
from app.models.user import User
from app.core import security
from app.core.config import settings
from app.db.session import SessionLocal
@ -26,12 +28,12 @@ def get_db() -> Generator:
def get_current_user(
db: Session = Depends(get_db), token: str = Depends(reusable_oauth2)
) -> models.User:
) -> User:
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
token_data = schemas.TokenPayload(**payload)
token_data = TokenPayload(**payload)
except (jwt.JWTError, ValidationError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
@ -44,16 +46,17 @@ def get_current_user(
def get_current_active_user(
current_user: models.User = Depends(get_current_user),
) -> models.User:
current_user: User = Depends(get_current_user),
) -> User:
if not crud.user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
def get_current_active_superuser(
current_user: models.User = Depends(get_current_user),
) -> models.User:
current_user: User = Depends(get_current_user),
) -> User:
if not crud.user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"

View File

@ -1,3 +1,4 @@
from .crud_user import user
from .crud_player import player
from .crud_team import team
from .crud_matchday import matchday

View File

@ -3,6 +3,7 @@ from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from sqlalchemy.orm import Session
from sqlmodel import select
from app.db.base_class import Base
@ -22,12 +23,14 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
self.model = model
def get(self, db: Session, id: Any) -> Optional[ModelType]:
return db.query(self.model).filter(self.model.id == id).first()
statement = select(self.model).where(self.model.id == id)
return db.execute(statement).scalar_one_or_none()
def get_multi(
self, db: Session, *, skip: int = 0, limit: int = 100
) -> List[ModelType]:
return db.query(self.model).offset(skip).limit(limit).all()
statement = select(self.model).offset(skip).limit(limit)
return db.execute(statement).scalars().all()
def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
obj_in_data = jsonable_encoder(obj_in)
@ -58,7 +61,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
return db_obj
def remove(self, db: Session, *, id: int) -> ModelType:
obj = db.query(self.model).get(id)
obj = db.execute(self.model).get(id)
db.delete(obj)
db.commit()
return obj

45
app/crud/crud_matchday.py Normal file
View File

@ -0,0 +1,45 @@
from typing import Any, Dict, Union, Optional
from sqlmodel import select
from sqlalchemy.orm import Session
from app.crud import player as crud_player
from app.crud.base import CRUDBase
from app.models.matchday import Matchday, MatchdayCreate, MatchdayUpdate
class CRUDMatchday(CRUDBase[Matchday, MatchdayCreate, MatchdayUpdate]):
def create(self, db: Session, *, obj_in: MatchdayCreate) -> Matchday:
db_obj = Matchday(
day=obj_in.day)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
def get_unique_day(self, db: Session, *, obj_in: Matchday) -> Any:
day = obj_in.day
statement = select(Matchday).where(Matchday.day == day)
result = db.execute(statement).all()
return result
def add_player_in_matchday(self, db: Session, matchday_id: int, player_id: int
) -> Matchday:
matchday_in = self.get(db=db, id=matchday_id)
db_player = crud_player.get(db=db, id=player_id)
matchday_in.players.append(db_player)
db.commit()
return matchday_in
def is_player_in_matchday(
self, db: Session, *, player_id: int, matchday_id: int
) -> bool:
matchday = self.get(db=db, id=matchday_id)
if matchday is None:
return False
db_player = crud_player.get(db=db, id=player_id)
return db_player in matchday.players
matchday = CRUDMatchday(Matchday)

View File

@ -3,8 +3,7 @@ from typing import Any, Dict, Optional, Union
from sqlalchemy.orm import Session
from app.crud.base import CRUDBase
from app.models.player import Player
from app.schemas.player import PlayerCreate, PlayerUpdate, PlayerUpdateTeam
from app.models.player import Player, PlayerCreate, PlayerUpdate
class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]):
@ -21,12 +20,6 @@ class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]):
.first()
)
def get_player(self, db: Session, *, player_id: int) -> Optional[Player]:
return db.query(Player).filter(Player.id == player_id).first()
def get_players(self, db: Session, skip: int = 0, limit: int = 100):
return db.query(Player).offset(skip).limit(limit).all()
def create(self, db: Session, *, obj_in: PlayerCreate) -> Player:
db_obj = Player(
firstname=obj_in.firstname,
@ -38,4 +31,5 @@ class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]):
return db_obj
player = CRUDPlayer(Player)

View File

@ -1,22 +1,14 @@
from typing import Any, Dict, Optional, Union, List
from sqlalchemy.orm import Session
from sqlmodel import select
from app.crud.base import CRUDBase
from app.crud import player as crud_player
from app.models.team import Team
from app.schemas.team import TeamCreate, TeamUpdate
from app.models.team import Team, TeamCreate, TeamUpdate
class CRUDTeam(CRUDBase[Team, TeamCreate, TeamUpdate]):
def get_team(self, db: Session, *, team_id: int) -> Optional[Team]:
return db.query(Team).filter(Team.team_id == team_id).first()
def get_teams(
self, db: Session, skip: int = 0, limit: int = 100
) -> List[Team]:
return db.query(Team).offset(skip).limit(limit).all()
def create(self, db: Session, *, obj_in: TeamCreate) -> Team:
db_obj = Team(teamname=obj_in.teamname)
@ -28,19 +20,19 @@ class CRUDTeam(CRUDBase[Team, TeamCreate, TeamUpdate]):
def add_player_in_team(
self, db: Session, team_id: int, player_id: int
) -> Team:
team = self.get_team(db=db, team_id=team_id)
db_player = crud_player.get_player(db=db, player_id=player_id)
team.players.append(db_player)
team_in = self.get(db=db, id=team_id)
db_player = crud_player.get(db=db, id=player_id)
team_in.players.append(db_player)
db.commit()
return team
return team_in
def is_player_in_team(
self, db: Session, *, player_id: int, team_id: int
) -> bool:
team = self.get_team(db=db, team_id=team_id)
team = self.get(db=db, id=team_id)
if team is None:
return False
db_player = crud_player.get_player(db=db, player_id=player_id)
db_player = crud_player.get(db=db, id=player_id)
return db_player in team.players

View File

@ -4,8 +4,8 @@ from sqlalchemy.orm import Session
from app.core.security import get_password_hash, verify_password
from app.crud.base import CRUDBase
from app.models.user import User
from app.schemas.user import UserCreate, UserUpdate
from app.models.user import User, UserCreate, UserUpdate
class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):

View File

@ -1,13 +1,12 @@
from typing import Any
from typing import Optional
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy.ext.declarative import declared_attr
from sqlmodel import Field, SQLModel
@as_declarative()
class Base:
id: Any
class Base(SQLModel):
id: Optional[int] = Field(default=None, primary_key=True, index=True)
__name__: str
# Generate __tablename__ automatically
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()

View File

@ -1,6 +1,6 @@
from sqlalchemy.orm import Session
from app import crud, schemas
from app import crud
from app.core.config import settings
from app.db import base # noqa: F401
@ -9,6 +9,9 @@ from app.db import base # noqa: F401
# for more details: https://github.com/tiangolo/full-stack-fastapi-postgresql/issues/28
from app.db.base_class import Base
from app.db.session import engine
from app.models.player import Player, PlayerCreate
from app.models.team import TeamCreate, TeamWithPlayers
from app.models.user import UserCreate
def init_db(db: Session) -> None:
@ -19,9 +22,34 @@ def init_db(db: Session) -> None:
user = crud.user.get_by_email(db, email=settings.FIRST_SUPERUSER)
if not user:
user_in = schemas.UserCreate(
user_in = UserCreate(
email=settings.FIRST_SUPERUSER,
password=settings.FIRST_SUPERUSER_PASSWORD,
is_superuser=True,
)
user = crud.user.create(db, obj_in=user_in) # noqa: F841
players = crud.player.get_multi(db=db)
if not players:
player_in = PlayerCreate(
firstname="Simon",
lastname="Milvert",
)
player = crud.player.create(db=db, obj_in=player_in) # noqa: F841
player_in = PlayerCreate(
firstname="Johan",
lastname="Moden")
player = crud.player.create(db=db, obj_in=player_in) # noqa: F841
teams = crud.team.get_multi(db=db)
if not True:
print("Create Team")
player = crud.player.get(db, id=1)
team_in = TeamWithPlayers(teamname='1', players=[player])
team = crud.team.create(db, obj_in=team_in) # noqa: F841

View File

@ -1,7 +1,13 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
from sqlmodel import create_engine, SQLModel
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

View File

@ -1,12 +1,18 @@
import logging
import uvicorn
from fastapi import FastAPI
from loguru import logger
from starlette.middleware.cors import CORSMiddleware
from app.api.api_v1.api import api_router
from app.core.config import settings
from app.db.init_db import init_db
from app.db.session import SessionLocal
from app.db.session import SessionLocal, create_db_and_tables
from app.utils import configure_log_handler
db = SessionLocal()
configure_log_handler(log_level=logging.DEBUG)
init_db(db)
@ -14,6 +20,10 @@ app = FastAPI(
title=settings.PROJECT_NAME,
openapi_url=f"{settings.API_V1_STR}/openapi.json",
)
@app.on_event("startup")
def on_startup():
create_db_and_tables()
# Set all CORS enabled origins
if settings.BACKEND_CORS_ORIGINS:
@ -28,3 +38,4 @@ if settings.BACKEND_CORS_ORIGINS:
)
app.include_router(api_router, prefix=settings.API_V1_STR)

View File

@ -1,5 +1,13 @@
# from .match import Match
# from .matchday import Matchday
from .player import Player
from .team import Team
from .user import User
from app.models.matchday import Matchday, MatchdayWithPlayers
from app.models.player import Player, PlayerTeamsMatchdays
from app.models.team import Team, TeamWithPlayers
Player.update_forward_refs(Team=Team, Matchday=Matchday)
PlayerTeamsMatchdays.update_forward_refs(Team=Team, Matchday=Matchday)
Team.update_forward_refs(Player=Player)
TeamWithPlayers.update_forward_refs(Player=Player)
Matchday.update_forward_refs(Player=Player)
MatchdayWithPlayers.update_forward_refs(Player=Player)

View File

@ -1,11 +1,14 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from typing import Optional
from app.db.database import Base
from sqlmodel import SQLModel, Field
from app.db.base_class import Base
class Match(Base):
class Match(SQLModel, Base, table=True):
matchname: Optional[str] = Field(nullable=False)
"""
match_id = Column(Integer, primary_key=True)
team_1 = Column(ForeignKey("team.id"), nullable=False)
team_2 = Column(ForeignKey("team.id"), nullable=False)
@ -18,3 +21,4 @@ class Match(Base):
team = relationship("Team", primaryjoin="Match.team_1 == Team.team_id")
team1 = relationship("Team", primaryjoin="Match.team_2 == Team.team_id")
team2 = relationship("Team", primaryjoin="Match.winner == Team.team_id")
"""

View File

@ -1,17 +1,36 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from datetime import date, datetime
from typing import List, Optional, TYPE_CHECKING
from sqlalchemy import Date
from sqlmodel import SQLModel, Relationship, Field, Column, func
from app.db.base_class import Base
from app.models.matchdayplayer import MatchdayPlayerLink
if TYPE_CHECKING:
from app.models.player import Player
class MatchdayPlayer(Base):
player_id = Column("players_id", ForeignKey("players.id"), primary_key=True)
matchday_id = Column("teams_id", ForeignKey("teams.id"), primary_key=True)
class MatchdayBase(SQLModel):
day: date
class Matchday(Base):
class Matchday(MatchdayBase, Base, table=True):
day: date = Field(
default=None,
sa_column=Column(Date, server_default=func.now()))
players: List["Player"] = Relationship(back_populates="matchdays",
link_model=MatchdayPlayerLink)
matchday_id = Column(Integer, primary_key=True)
day = Column(DateTime, nullable=False)
players = relationship("Player", secondary="matchdayplayer")
class MatchdayCreate(MatchdayBase):
day: date
class MatchdayUpdate(MatchdayBase):
day: date
class MatchdayWithPlayers(MatchdayBase):
day: date
players: Optional[List["Player"]]

View File

@ -0,0 +1,11 @@
from typing import Optional
from sqlmodel import SQLModel, Field
class MatchdayPlayerLink(SQLModel, table=True):
player_id: Optional[int] = Field(
default=None, foreign_key="player.id", primary_key=True
)
matchday_id: Optional[int] = Field(
default=None, foreign_key="matchday.id", primary_key=True
)

View File

@ -1,16 +1,35 @@
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from typing import Optional, List, TYPE_CHECKING
from sqlmodel import SQLModel, Field, Relationship
from app.db.base_class import Base
from app.models.matchdayplayer import MatchdayPlayerLink
from app.models.teamplayers import TeamPlayerLink
class Player(Base):
if TYPE_CHECKING:
from app.models import Team, Matchday
class PlayerBase(SQLModel):
firstname: str = Field(nullable=False)
lastname: str = Field(nullable=False)
id = Column(Integer, primary_key=True, index=True)
firstname = Column(String, nullable=True)
lastname = Column(String, nullable=True)
team_id = Column(Integer, ForeignKey("team.team_id"))
team = relationship("Team", back_populates="players")
team_id = Column(Integer, ForeignKey("team.team_id"))
team = relationship("Team", back_populates="players")
class Player(PlayerBase, Base, table=True):
teams: List["Team"] = Relationship(back_populates="players",
link_model=TeamPlayerLink)
matchdays: List["Matchday"] = Relationship(back_populates="players",
link_model=MatchdayPlayerLink)
class PlayerCreate(PlayerBase):
firstname: str
lastname: str
class PlayerUpdate(PlayerBase):
firstname: str
lastname: str
class PlayerTeamsMatchdays(PlayerBase):
matchdays: List["Matchday"]
teams: List["Team"]

View File

@ -1,11 +1,34 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from typing import Optional, List, TYPE_CHECKING
from sqlmodel import SQLModel, Field, Relationship
from app.db.base_class import Base
from app.models.teamplayers import TeamPlayerLink
if TYPE_CHECKING:
from app.models.player import Player
class TeamBase(SQLModel):
teamname: Optional[str] = Field(nullable=False)
class Team(TeamBase, Base, table=True):
players: List["Player"] = Relationship(back_populates="teams",
link_model=TeamPlayerLink
)
class TeamCreate(TeamBase):
teamname: str
class TeamWithPlayers(TeamBase):
teamname: str
players: List["Player"]
class TeamUpdate(TeamBase):
teamname: str
class Team(Base):
team_id = Column(Integer, primary_key=True, index=True)
teamname = Column(String, nullable=False)
players = relationship("Player", back_populates="team")

12
app/models/teamplayers.py Normal file
View File

@ -0,0 +1,12 @@
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship
class TeamPlayerLink(SQLModel, table=True):
team_id: Optional[int] = Field(
default=None, foreign_key="team.id", primary_key=True
)
player_id: Optional[int] = Field(
default=None, foreign_key="player.id", primary_key=True
)

View File

@ -1,8 +1,6 @@
from typing import Optional
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
token_type: str

View File

@ -1,12 +1,29 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Boolean
from typing import Optional
from pydantic import EmailStr
from sqlmodel import SQLModel, Field
from app.db.base_class import Base
class User(Base):
id = Column(Integer, primary_key=True, index=True)
full_name = Column(String, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
is_active = Column(Boolean(), default=True)
is_superuser = Column(Boolean(), default=False)
class UserBase(SQLModel):
full_name: Optional[str] = Field(index=True)
email: Optional[EmailStr] = Field(unique=True, index=True, nullable=False)
hashed_password: Optional[str] = Field(nullable=False)
is_active: Optional[bool] = Field(default=True)
is_superuser: bool = Field(default=False)
class User(UserBase, Base, table=True):
pass
class UserCreate(UserBase):
email: EmailStr
password: str
# Properties to receive via API on update
class UserUpdate(UserBase):
password: Optional[str] = None

View File

@ -1,5 +1,3 @@
from .user import User, UserCreate, UserInDB, UserUpdate
from .token import Token, TokenPayload
from .msg import Msg
from .player import Player, PlayerCreate, PlayerInDB
from .team import Team, TeamCreate, TeamInDBBase
#from .user import User, UserCreate, UserInDB, UserUpdate
#from .player import Player, PlayerCreate, PlayerInDB
#from .team import Team, TeamCreate, TeamInDBBase

View File

@ -1,41 +0,0 @@
from typing import Optional
from pydantic import BaseModel, EmailStr
# Shared properties
class PlayerBase(BaseModel):
firstname: Optional[str] = None
lastname: Optional[str] = None
# Properties to receive via API on creation
class PlayerCreate(PlayerBase):
firstname: str
lastname: str
class PlayerInDBBase(PlayerBase):
id: Optional[int] = None
class Config:
orm_mode = True
class PlayerUpdate(PlayerBase):
firstname: str
lastname: str
class PlayerUpdateTeam(PlayerBase):
team_id: Optional[int]
# Additional properties to return via API
class Player(PlayerInDBBase):
pass
# Additional properties stored in DB
class PlayerInDB(PlayerInDBBase):
pass

View File

@ -1,39 +0,0 @@
from typing import Optional
from pydantic import BaseModel
# Shared properties
from app.schemas import Player
class TeamBase(BaseModel):
teamname: Optional[str] = None
# Properties to receive via API on creation
class TeamCreate(TeamBase):
teamname: Optional[str]
class TeamInDBBase(TeamBase):
players: list[Player] = []
class Config:
orm_mode = True
class TeamUpdate(TeamBase):
pass
# Additional properties to return via API
class Team(TeamInDBBase):
team_id: Optional[int] = None
players: list[Player] = []
# Additional properties stored in DB
class TeamInDB(TeamInDBBase):
pass

View File

@ -1,39 +0,0 @@
from typing import Optional
from pydantic import BaseModel, EmailStr
# Shared properties
class UserBase(BaseModel):
email: Optional[EmailStr] = None
is_active: Optional[bool] = True
is_superuser: bool = False
full_name: Optional[str] = None
# Properties to receive via API on creation
class UserCreate(UserBase):
email: EmailStr
password: str
# Properties to receive via API on update
class UserUpdate(UserBase):
password: Optional[str] = None
class UserInDBBase(UserBase):
id: Optional[int] = None
class Config:
orm_mode = True
# Additional properties to return via API
class User(UserInDBBase):
pass
# Additional properties stored in DB
class UserInDB(UserInDBBase):
hashed_password: str

View File

@ -8,6 +8,44 @@ from emails.template import JinjaTemplate
from jose import jwt
from app.core.config import settings
import sys
import logging
from loguru import logger
logger.remove()
logger.add(
sys.stdout,
format="<green>{time:YYYY/MM/DD HH:mm:ss}</green> <level>{level: <5} <cyan>{name}</cyan> {message}</level>",
level="DEBUG",
)
class InterceptHandler(logging.Handler):
def emit(self, record):
# Get corresponding Loguru level if it exists.
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
# Find caller from where originated the logged message.
frame, depth = sys._getframe(6), 6
while frame and frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
logger.opt(
depth=depth, exception=record.exc_info
).log(
"DEBUG", record.getMessage()
)
def configure_log_handler(*, log_level=logging.WARNING):
logging.basicConfig(handlers=[InterceptHandler()],
level=logging.NOTSET, force=True)
for target in ['sqlalchemy.engine.Engine', 'sqlalchemy.engine']:
logging.getLogger(target).setLevel(log_level)
def send_email(

13
debug_run.py Normal file
View File

@ -0,0 +1,13 @@
import uvicorn
from loguru import logger
from app.main import app
def main():
uvicorn.run("debug_run:app", host="0.0.0.0", port=1234, reload=True)
if __name__ == "__main__":
logger.info("begin")
main()
logger.info("done")

Binary file not shown.

68
poetry.lock generated
View File

@ -383,6 +383,21 @@ category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "loguru"
version = "0.6.0"
description = "Python logging made (stupidly) simple"
category = "main"
optional = false
python-versions = ">=3.5"
[package.dependencies]
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
[[package]]
name = "lxml"
version = "4.9.1"
@ -754,6 +769,30 @@ postgresql_psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"]
sqlcipher = ["sqlcipher3-binary"]
[[package]]
name = "sqlalchemy2-stubs"
version = "0.0.2a27"
description = "Typing Stubs for SQLAlchemy 1.4"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
typing-extensions = ">=3.7.4"
[[package]]
name = "sqlmodel"
version = "0.0.8"
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
category = "main"
optional = false
python-versions = ">=3.6.1,<4.0.0"
[package.dependencies]
pydantic = ">=1.8.2,<2.0.0"
SQLAlchemy = ">=1.4.17,<=1.4.41"
sqlalchemy2-stubs = "*"
[[package]]
name = "starlette"
version = "0.19.1"
@ -868,6 +907,17 @@ platformdirs = ">=2.4,<3"
docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"]
testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]
[[package]]
name = "win32-setctime"
version = "1.1.0"
description = "A small Python utility to set file creation time on Windows"
category = "main"
optional = false
python-versions = ">=3.5"
[package.extras]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "wrapt"
version = "1.14.1"
@ -879,7 +929,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "875e82447867f92eb96260ad048da2e4967d2c8285ecaac9dca37dd839d7d78f"
content-hash = "f51b93ad173b06640d39b5143150dd0219b20bfcd5c3c9a0a4036b5638d12925"
[metadata.files]
anyio = [
@ -1257,6 +1307,10 @@ lazy-object-proxy = [
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
{file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
]
loguru = [
{file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
{file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
]
lxml = [
{file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"},
{file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"},
@ -1536,6 +1590,14 @@ sqlalchemy = [
{file = "SQLAlchemy-1.4.40-cp39-cp39-win_amd64.whl", hash = "sha256:bf073c619b5a7f7cd731507d0fdc7329bee14b247a63b0419929e4acd24afea8"},
{file = "SQLAlchemy-1.4.40.tar.gz", hash = "sha256:44a660506080cc975e1dfa5776fe5f6315ddc626a77b50bf0eee18b0389ea265"},
]
sqlalchemy2-stubs = [
{file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"},
{file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"},
]
sqlmodel = [
{file = "sqlmodel-0.0.8-py3-none-any.whl", hash = "sha256:0fd805719e0c5d4f22be32eb3ffc856eca3f7f20e8c7aa3e117ad91684b518ee"},
{file = "sqlmodel-0.0.8.tar.gz", hash = "sha256:3371b4d1ad59d2ffd0c530582c2140b6c06b090b32af9b9c6412986d7b117036"},
]
starlette = [
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
@ -1572,6 +1634,10 @@ virtualenv = [
{file = "virtualenv-20.16.3-py2.py3-none-any.whl", hash = "sha256:4193b7bc8a6cd23e4eb251ac64f29b4398ab2c233531e66e40b19a6b7b0d30c1"},
{file = "virtualenv-20.16.3.tar.gz", hash = "sha256:d86ea0bb50e06252d79e6c241507cb904fcd66090c3271381372d6221a3970f9"},
]
win32-setctime = [
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
wrapt = [
{file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
{file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},

View File

@ -14,6 +14,8 @@ python-jose = {extras = ["cryptography"], version = "^3.3.0"}
pydantic = {extras = ["email"], version = "^1.10.0"}
python-multipart = "^0.0.5"
emails = "^0.6"
sqlmodel = "^0.0.8"
loguru = "^0.6.0"
[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
@ -30,6 +32,8 @@ isort = "^5.10.1"
line-length = 79
target-version = [ "py39",]
[tool.poetry.scripts]
web = "app.main:"
[build-system]
requires = ["poetry-core>=1.0.0"]