Git Commit: create user functionality
Time for our first real API route. Till now we have made very simple routes, This time we will be using pydantic schemas, database connections, and dependencies all at one endpoint. We are going to make a route that will enable the creation of users. Why are we doing this first? It's because, in our 'Blog' model, we have a foreign key to the user's table. So, we need an owner_id which is basically the id of the user model. Let's jump into the code but before that create this new folder structure:
backend/
├─.env
├─alembic/
├─apis/
│ ├─base.py #new file
│ └─v1/ #new folder
│ └─route_user.py #new file
├─core/
│ └─config.py
├─db/
│ ├─base.py
│ ├─base_class.py
│ ├─models/
│ ├─repository/ #new folder
│ │ └─user.py #new file
│ └─session.py
├─alembic.ini
├─main.py
├─requirements.txt
├─schemas/
Now, we need to type the below lines in apis > v1 > route_user.py
from fastapi import APIRouter
from sqlalchemy.orm import Session
from fastapi import Depends
from schemas.user import UserCreate
from db.session import get_db
from db.repository.user import create_new_user
router = APIRouter()
@router.post("/")
def create_user(user : UserCreate,db: Session = Depends(get_db)):
user = create_new_user(user=user,db=db)
return user
Type the following in db > repository > user.py
from sqlalchemy.orm import Session
from schemas.user import UserCreate
from db.models.user import User
from core.hashing import Hasher
def create_new_user(user:UserCreate,db:Session):
user = User(
email = user.email,
password=Hasher.get_password_hash(user.password),
is_active=True,
is_superuser=False
)
db.add(user)
db.commit()
db.refresh(user)
return user
from fastapi import APIRouter
from apis.v1 import route_user
api_router = APIRouter()
api_router.include_router(route_user.router,prefix="",tags=["users"])
Just one step more, make sure we import this 'api_router' in the main.py file and include it with our app: main.py
from apis.base import api_router #new
###
def include_router(app):
app.include_router(api_router)
def start_application():
app = FastAPI(title=settings.PROJECT_NAME,version=settings.PROJECT_VERSION)
create_tables()
include_router(app) #new
return app
All done. Let's verify its working:
{
"password": "$2b$12$qsPsF3.JVAwWYqsioLvuJOOSiJCZuWRv5yCBCuFdumoA1bYRlXhCO",
"email": "[email protected]",
"is_superuser": false,
"id": 3,
"is_active": true
}
We are passing hashed_password, id, and is_superuser status, These are implementation details and from a security point of view, we should not pass these data. Let's restrict our response to only username, email, and is_active status. Remember pydantic schemas? They are used to validate request data. Similarly, they can be used to restrict data to have only a limited number of fields.
Let's add a new schema in schemas > user.py
from pydantic import BaseModel,EmailStr, Field
#properties required during user creation
class UserCreate(BaseModel):
email : EmailStr
password : str = Field(..., min_length=4)
#new
class ShowUser(BaseModel):
id: int
email : EmailStr
is_active : bool
class Config(): #tells pydantic to convert even non dict obj to json
orm_mode = True
and we need to specify this ShowUser schema as a response_model of our post route. I believe it would be better to call it request_schema!! Ok, now we modify our apis > version1 > route_users.py as follows:
...
from fastapi import APIRouter, status #modified
from schemas.users import UserCreate,ShowUser #modified
from db.session import get_db
...
router = APIRouter()
@router.post("/",response_model = ShowUser, status_code=status.HTTP_201_CREATED) #modified
def create_user(user : UserCreate,db: Session = Depends(get_db)):
user = create_new_user(user=user,db=db)
return user
All done, now we are not passing hashed password, is_superuser in the response.
Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.
We know, we might make it hard for you but definitely worth the efforts.
© Copyright 2022-23 Team FastAPITutorial