Skip to content

Commit 82b1dcb

Browse files
committed
feat: upgrade pydantic versin with python version and made changes for pydantic syntax
1 parent 5959497 commit 82b1dcb

File tree

12 files changed

+270
-187
lines changed

12 files changed

+270
-187
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ To get a local copy up and running follow these simple steps.
5858
Requirement of Project
5959
* Install Python
6060
```sh
61-
Python-Version : 3.10.13
61+
Python-Version : 3.11.0
6262
```
6363
* Create python virtual environment
6464
```sh
@@ -73,7 +73,7 @@ Requirement of Project
7373

7474
1. Clone the repo
7575
```sh
76-
git clone https://github.com/viitoradmin/python-fastapi-boilerplate
76+
git clone https://github.com/viitoradmin/python-fastapi-boilerplate/tree/feature/fastapi
7777
```
7878
2. Upgrade pip version
7979
```sh
@@ -92,7 +92,7 @@ Requirement of Project
9292

9393
1. Create python virtual environment
9494
```
95-
conda create --name venv python=3.10.12
95+
conda create --name venv python=3.11
9696
```
9797
9898
2. Activate the python virtual environment
@@ -161,6 +161,8 @@ Run the server
161161
```
162162
Browse Swagger API Doc at: http://localhost:8000/docs
163163
Browse Redoc at: http://localhost:8000/redoc
164+
Browse Swagger API Doc for version v1 at: http://localhost:8000/v1/docs
165+
Browse Swagger API Doc for version v2 at: http://localhost:8000/v2/docs
164166
165167
## Release History
166168

apps/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from config import database
44
from apps.constant import constant
55
from fastapi_versioning import VersionedFastAPI
6-
from apps.api.auth.view import defaultrouter, router
6+
from apps.api.auth.view import router_v1, router_v2
77
from apps.api.auth.models import Base as authbase
88

99
# Bind with the database, whenever new models find it's create it.
@@ -15,13 +15,14 @@
1515
# define router for different version
1616
# router for version 1
1717
app.include_router(
18-
defaultrouter,
18+
router_v1,
1919
prefix=constant.API_V1, tags=["/v1"]
2020
)
2121
# router for version 2
2222
app.include_router(
23-
router, prefix=constant.API_V2, tags=["/v2"]
24-
)
23+
router_v2,
24+
prefix=constant.API_V2, tags=["/v2"]
25+
)
2526

2627
# Define version to specify version related API's.
27-
app = VersionedFastAPI(app, version_format="{major}", prefix_format="/v{major}", enable_latest=True)
28+
app = VersionedFastAPI(app, version_format="{major}", prefix_format="/v{major}")

apps/api/auth/method.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from sqlalchemy.orm import Session
2+
from apps.constant import constant
3+
4+
class UserAuthMethod():
5+
"""This class defines methods to authenticate users."""
6+
7+
def __init__(self, model) -> constant.STATUS_NULL:
8+
self.model = model
9+
10+
def find_by_email(self, db: Session, email: str):
11+
"""This funtion will return the email object"""
12+
return db.query(self.model).filter(
13+
self.model.email == email
14+
).first()
15+
16+
def find_by_username(self, db: Session, username: str):
17+
"""This function will return the username object"""
18+
return db.query(self.model).filter(
19+
self.model.username == username
20+
).first()

apps/api/auth/schema.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
11
"""This module is for swager and request parameter schema"""
2-
from pydantic import BaseModel, Extra
2+
from pydantic import BaseModel, Extra, validator, Field
3+
from apps.api.core import validation
34

45

56
class UserAuth(BaseModel):
6-
first_name: str
7-
last_name: str
8-
email: str
9-
password: str
10-
username: str
7+
first_name: str
8+
last_name: str
9+
email: str
10+
password: str
11+
username: str
1112

1213
class Config:
13-
extra = Extra.forbid
14-
orm_mode = True
15-
extra = Extra.allow
16-
schema_extra = {
14+
from_attributes = True
15+
json_schema_extra = {
1716
"example": {
1817
"first_name": "John",
1918
"last_name": "Smith",
2019
"email": "jhohnsmith@example.com",
2120
"password": "Abc@123",
2221
"username": "Jhon123"
2322
}
24-
}
23+
}
24+
25+
@validator('first_name', pre=True)
26+
def first_name_must_be_required(cls, v):
27+
return validation.ValidationMethods().not_null_validator(v, 'first_name')
28+
29+
@validator('last_name', allow_reuse=True)
30+
def last_name_must_be_required(cls, v):
31+
return validation.ValidationMethods().not_null_validator(v, 'last_name')
32+
33+
@validator('email', allow_reuse=True)
34+
def email_must_be_required(cls, v):
35+
return validation.ValidationMethods().not_null_validator(v, 'email')
36+
37+
@validator('username', allow_reuse=True)
38+
def username_must_be_required(cls, v):
39+
return validation.ValidationMethods().not_null_validator(v, 'username')
40+
41+
@validator('email', allow_reuse=True)
42+
def email_field_validator(cls, v):
43+
return validation.ValidationMethods().email_validator(v)

apps/api/auth/service.py

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,102 @@
1-
from apps.utils.standard_response import StandardResponse
1+
import uuid
22
from fastapi import status
33
from apps.constant import constant
4+
from sqlalchemy.orm import Session
5+
from apps.api.auth.models import Users
6+
from apps.api.core import db_methods
7+
from fastapi.encoders import jsonable_encoder
8+
from apps.api.auth.method import UserAuthMethod
9+
from apps.api.core.validation import ValidationMethods
10+
from apps.utils.message import ErrorMessage, InfoMessage
11+
from apps.utils.standard_response import StandardResponse
12+
413

5-
def get_str_name(name: str):
6-
if name is not None:
14+
class UserAuthService:
15+
"""This class represents the user creation service"""
16+
def create_user_service(self, db: Session, body: dict):
17+
"""This function is used to create user
18+
Args:
19+
db (Session): database connection
20+
body (dict): dictionary to user information data
21+
22+
Returns:
23+
response (dict): user object representing the user
24+
"""
25+
username = body['username']
26+
email = body['email'] or None
27+
password = body['password']
28+
29+
# check Email exists in body or not
30+
if "email" not in body or not email:
31+
return StandardResponse(
32+
False, status.HTTP_400_BAD_REQUEST, None, ErrorMessage.emailRequired
33+
)
34+
35+
# check Email exists in Db or not
36+
if (user_object := UserAuthMethod(Users).find_by_email(db, email)):
37+
return StandardResponse(
38+
False, status.HTTP_400_BAD_REQUEST,
39+
constant.STATUS_NULL, ErrorMessage.emailAlreadyExist
40+
).make
41+
42+
# For password validation
43+
if not ValidationMethods().password_validator(password):
44+
return StandardResponse(
45+
False, status.HTTP_400_BAD_REQUEST,
46+
constant.STATUS_NULL, ErrorMessage.invalidPasswordFormat
47+
).make
48+
49+
user_object = Users(
50+
uuid = uuid.uuid4(),
51+
first_name=body['first_name'],
52+
last_name=body['last_name'],
53+
username=username,
54+
email=email,
55+
password=password
56+
)
57+
58+
# Store user object in database
59+
if not(user_save := db_methods.BaseMethods(Users).save(user_object, db)):
60+
return StandardResponse(
61+
False, status.HTTP_400_BAD_REQUEST,
62+
constant.STATUS_NULL, ErrorMessage.userNotSaved
63+
)
64+
65+
# check email exists or not
66+
if user_object := UserAuthMethod(Users).find_by_email(db, email):
67+
data = {
68+
"username": user_object.username,
69+
"first_name": user_object.first_name,
70+
"last_name": user_object.last_name,
71+
"email": user_object.email,
72+
"password": user_object.password,
73+
}
74+
75+
else:
76+
return StandardResponse(
77+
False, status.HTTP_400_BAD_REQUEST,
78+
constant.STATUS_NULL, ErrorMessage.userInvalid
79+
).make
80+
781
return StandardResponse(
8-
True, status.HTTP_200_OK, {"success": "Welcome"},
9-
constant.STATUS_SUCCESS
82+
True, status.HTTP_200_OK, data, InfoMessage.userCreated
83+
).make
84+
85+
def get_user_service(self, db):
86+
"""This function returns the user service list."""
87+
if not (user_object := db_methods.BaseMethods(Users).find_by_uuid(db)):
88+
return StandardResponse(
89+
False,
90+
status.HTTP_400_BAD_REQUEST,
91+
None,
92+
ErrorMessage.userNotFound
1093
)
11-
else:
94+
# convert the object data into json
95+
user_data = jsonable_encoder(user_object)
96+
1297
return StandardResponse(
13-
True, status.HTTP_400_BAD_REQUEST, {"success": "Error"},
14-
constant.STATUS_ERROR
15-
)
98+
True,
99+
status.HTTP_200_OK,
100+
user_data,
101+
InfoMessage.retriveInfoSuccessfully
102+
)

apps/api/auth/view.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,61 @@
22
from config import database
33
from fastapi import Depends
44
from sqlalchemy.orm import Session
5-
from fastapi_utils.cbv import cbv
65
from apps.api.auth import schema
76
from fastapi import APIRouter
87
from apps.constant import constant
98
from fastapi_versioning import version
10-
from fastapi_utils.inferring_router import InferringRouter
9+
from apps.api.auth.service import UserAuthService
1110
from apps.utils.standard_response import StandardResponse
1211

1312
## Load API's
14-
defaultrouter = APIRouter()
1513
router = APIRouter()
14+
router_v1 = APIRouter()
1615
getdb = database.get_db
1716

1817
## Define verison 1 API's here
19-
@cbv(defaultrouter)
2018
class UserCrudApi():
2119
"""This class is for user's CRUD operation with version 1 API's"""
2220

23-
@defaultrouter.get('/list/user')
21+
@router_v1.get('/users')
2422
@version(1)
25-
async def list_user(self):
23+
async def list_user(db: Session = Depends(getdb)):
2624
"""This API is for list user.
2725
Args: None
2826
Returns:
29-
response: will return list."""
27+
response: will return users list."""
3028
try:
31-
data = {"List:" : "Hello there, welcome to fastapi bolierplate"}
32-
return StandardResponse(True, status.HTTP_200_OK, data, constant.STATUS_SUCCESS)
29+
response = UserAuthService().get_user_service(db)
30+
return response
3331
except Exception as e:
3432
return StandardResponse(False, status.HTTP_400_BAD_REQUEST, None, constant.ERROR_MSG)
3533

36-
@defaultrouter.post('/create/user')
34+
@router_v1.post('/create/user')
3735
@version(1)
38-
async def create_user(self, body: schema.UserAuth,
36+
async def create_user(body: schema.UserAuth,
3937
db: Session = Depends(getdb)):
4038
"""This API is for create user.
4139
Args:
4240
body(dict) : user's data
4341
Returns:
4442
response: will return the user's data"""
4543
try:
46-
data = body.dict()
47-
return StandardResponse(True, status.HTTP_200_OK, data, constant.STATUS_SUCCESS)
44+
# as per pydantic version 2.
45+
body = body.model_dump()
46+
response = UserAuthService().create_user_service(db, body)
47+
return response
4848
except Exception as e:
4949
return StandardResponse(False, status.HTTP_400_BAD_REQUEST, None, constant.ERROR_MSG)
5050

5151

5252
## Define version 2 API's here
53-
@cbv(router)
53+
router_v2 = APIRouter()
54+
5455
class UserVersionApi():
55-
@router.get("/list")
56+
"""This class provides version 2 API's for users"""
57+
@router_v2.get("/list")
5658
@version(2)
57-
async def get_list(self):
59+
async def get_list():
5860
""" This API will list version 2 Api's
5961
Args: None
6062
Returns:

apps/api/core/db_methods.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from fastapi import Depends
2+
from config import database
3+
from apps.constant import constant
4+
from sqlalchemy.orm import Session
5+
6+
getdb = database.get_db
7+
8+
class BaseMethods():
9+
"""This class provides basic DB methods"""
10+
11+
def __init__(self, model):
12+
self.model = model
13+
14+
def save(self, validate_data, db: Session = Depends(getdb)):
15+
"""this function saves the object to the database for the given model
16+
Args:
17+
validate_data (dict): validate data
18+
db (Session): database session.
19+
Returns:
20+
returns the created object
21+
"""
22+
try:
23+
db.add(validate_data)
24+
db.commit()
25+
db.refresh(validate_data)
26+
return constant.STATUS_TRUE
27+
except Exception as err:
28+
print(err)
29+
db.rollback()
30+
db.close()
31+
return constant.STATUS_FALSE
32+
33+
def find_by_uuid(self, db: Session = Depends(getdb)):
34+
"""This function is used to find users."""
35+
return db.query(self.model).filter(self.model.deleted_at == None).all()
36+

0 commit comments

Comments
 (0)