added crud Users

parent 7a872ebf
from fastapi import APIRouter from fastapi import APIRouter
from routes import router as user_route
router = APIRouter(prefix='/api', tags=['AUTH']) router = APIRouter(prefix='/api', tags=['AUTH'])
router.include_router(user_route)
import os
db_url = os.getenv('DATABASE_URL')
from uuid import UUID
from repository import UserRepository
from exceptions import common as common_exc, http as http_exc
class BaseController:
repo = None
async def get_list(self, **kwargs) -> list[dict]:
return await self.repo.get_list(**kwargs)
async def get(self, id: UUID) -> dict:
try:
return await self.repo.get(id)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
async def create(self, **data) -> dict:
try:
return await self.repo.create(**data)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
async def update(self, id: UUID, **kwargs) -> dict:
try:
return await self.repo.update(id, **kwargs)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
except common_exc.UpdateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
async def delete(self, id: UUID) -> None:
try:
return await self.repo.delete(id)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
except common_exc.DeleteException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
class UserController(BaseController):
repo = UserRepository()
import os
from fastapi import FastAPI from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.fastapi import register_tortoise
from const import db_url
TORTOISE_ORM = { TORTOISE_ORM = {
'connections': {'default': db_url}, 'connections': {'default': os.getenv('DATABASE_URL')},
'apps': { 'apps': {
'models': { 'models': {
'models': ['code.models', 'aerich.models'], 'models': ['code.models', 'aerich.models'],
...@@ -18,7 +18,7 @@ TORTOISE_ORM = { ...@@ -18,7 +18,7 @@ TORTOISE_ORM = {
def init_db(app: FastAPI) -> None: def init_db(app: FastAPI) -> None:
register_tortoise( register_tortoise(
app, app,
db_url=db_url, db_url=os.getenv('DATABASE_URL'),
modules={"models": ["models"]}, modules={"models": ["models"]},
generate_schemas=False, generate_schemas=False,
add_exception_handlers=True, add_exception_handlers=True,
......
class NotFoundException(Exception):
pass
class CreateException(Exception):
pass
class UpdateException(Exception):
pass
class DeleteException(Exception):
pass
import fastapi
import pydantic
from starlette import status
from fastapi.responses import JSONResponse
from .http import BaseHTTPException
async def query_params_exc_handler(
request: fastapi.Request, exc: pydantic.ValidationError,
) -> JSONResponse:
return JSONResponse(
{'detail': exc.errors()}, status.HTTP_422_UNPROCESSABLE_ENTITY,
)
async def request_exc_handler(
request: fastapi.Request, exc: BaseHTTPException,
) -> JSONResponse:
return JSONResponse(
{'detail': exc.detail}, exc.status_code,
)
async def internal_exc_handler(
request: fastapi.Request, exc: Exception,
) -> JSONResponse:
return JSONResponse(
{'detail': 'Internal Server Error'},
status.HTTP_500_INTERNAL_SERVER_ERROR,
)
from typing import Any, Dict
from fastapi.exceptions import HTTPException
from starlette import status
class BaseHTTPException(HTTPException):
pass
class HTTPBadRequestException(BaseHTTPException):
def __init__(
self, status_code: int = status.HTTP_400_BAD_REQUEST, detail: Any = None,
headers: Dict[str, str] | None = None
) -> None:
super().__init__(status_code, detail, headers)
class HTTPNotFoundException(BaseHTTPException):
def __init__(
self, status_code: int = status.HTTP_404_NOT_FOUND, detail: Any = None,
headers: Dict[str, str] | None = None
) -> None:
super().__init__(status_code, detail, headers)
from uuid import uuid4
from typing import Type, Any
from passlib.context import CryptContext
from tortoise import fields, models from tortoise import fields, models
from tortoise.models import MODEL
class User(models.Model): class User(models.Model):
id = fields.UUIDField(pk=True, default=uuid4)
username = fields.CharField(max_length=100, unique=True) username = fields.CharField(max_length=100, unique=True)
fullname = fields.CharField(max_length=100) name = fields.CharField(max_length=100)
surname = fields.CharField(max_length=100)
email = fields.CharField(max_length=100)
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
password = fields.CharField(max_length=255) password = fields.CharField(max_length=255)
def __str__(self): def __str__(self):
return self.fullname return self.name
class Meta: class Meta:
table = 'users' table = 'users'
@staticmethod
def get_password_hash(password):
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
return pwd_context.hash(password)
@classmethod
async def create(cls: Type[MODEL], **kwargs: Any) -> MODEL:
kwargs['password'] = cls.get_password_hash(kwargs['password'])
return await super().create(**kwargs)
from uuid import UUID
import tortoise
from exceptions import common as common_exc
from models import User
class BaseRepository:
model: tortoise.Model
async def get_list(self, **kwargs) -> list[tortoise.Model]:
return await self.model.filter(**kwargs)
async def get(self, id: UUID) -> tortoise.Model:
try:
return await self.model.get(id=id)
except tortoise.exceptions.DoesNotExist as e:
raise common_exc.NotFoundException(str(e))
async def create(self, **kwargs) -> tortoise.Model:
try:
return await self.model.create(**kwargs)
except tortoise.exceptions.IntegrityError as e:
raise common_exc.CreateException(str(e))
async def update(self, id: UUID, **kwargs) -> tortoise.Model:
try:
instance = await self.get(id=id)
await instance.update_from_dict(kwargs).save()
return instance
except tortoise.exceptions.IntegrityError as e:
raise common_exc.UpdateException(str(e))
async def delete(self, id: UUID) -> None:
try:
instance = await self.get(id=id)
await instance.delete()
except tortoise.exceptions.IntegrityError as e:
raise common_exc.DeleteException(str(e))
class UserRepository(BaseRepository):
model = User
from uuid import UUID
import fastapi
from schemas import (
UserAllOptionalSchema, CreateUserSchema, CreatedUserResponseSchema,
UpdateUserSchema
)
from ctrl import UserController
router = fastapi.APIRouter(prefix='/users', tags=['AUTH'])
ctrl = UserController()
@router.get('')
async def get_users(query: UserAllOptionalSchema = fastapi.Depends()):
return await ctrl.get_list(**query.model_dump(exclude_none=True))
@router.get('/{id}')
async def get_users(id: UUID):
return await ctrl.get(id)
@router.post('')
async def create_users(
body: CreateUserSchema
) -> CreatedUserResponseSchema:
return await ctrl.create(**body.model_dump(exclude_none=True))
@router.patch('/{id}')
async def update_users(
id: UUID, body: UpdateUserSchema,
) -> CreatedUserResponseSchema:
return await ctrl.update(id, **body.model_dump(exclude_none=True))
@router.delete('/{id}')
async def delete_users(
id: UUID,
):
return await ctrl.delete(id)
from uuid import UUID
from pydantic import BaseModel, EmailStr
class CreateUserSchema(BaseModel):
username: str
name: str
surname: str
email: EmailStr
password: str
class CreatedUserResponseSchema(BaseModel):
id: UUID
username: str
name: str
surname: str
email: str
class UpdateUserSchema(BaseModel):
name: str
surname: str
email: str
class UserAllOptionalSchema(BaseModel):
username: None | str = None
password: None | str = None
...@@ -17,6 +17,17 @@ services: ...@@ -17,6 +17,17 @@ services:
- .env - .env
command: bash -c "python3 code/main.py" command: bash -c "python3 code/main.py"
db:
image: postgres:14-alpine
env_file:
- .env
ports:
- "5436:5432"
volumes:
- ./data/postgres:/var/lib/postgresql/data/
networks:
- tasktrekernetwork
networks: networks:
tasktrekernetwork: tasktrekernetwork:
driver: bridge driver: bridge
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
CREATE TABLE IF NOT EXISTS "users" (
"id" UUID NOT NULL PRIMARY KEY,
"username" VARCHAR(100) NOT NULL UNIQUE,
"name" VARCHAR(100) NOT NULL,
"surname" VARCHAR(100) NOT NULL,
"email" VARCHAR(100) NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"password" VARCHAR(255) NOT NULL
);
CREATE TABLE IF NOT EXISTS "aerich" (
"id" SERIAL NOT NULL PRIMARY KEY,
"version" VARCHAR(255) NOT NULL,
"app" VARCHAR(100) NOT NULL,
"content" JSONB NOT NULL
);"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
"""
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "users" ADD "updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP;"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "users" DROP COLUMN "updated_at";"""
[tool.aerich]
tortoise_orm = "code.db.TORTOISE_ORM"
location = "./migrations"
src_folder = "./."
aerich==0.7.2
aiosqlite==0.17.0 aiosqlite==0.17.0
annotated-types==0.6.0 annotated-types==0.6.0
anyio==3.7.1 anyio==3.7.1
...@@ -8,7 +9,10 @@ certifi==2023.11.17 ...@@ -8,7 +9,10 @@ certifi==2023.11.17
cffi==1.16.0 cffi==1.16.0
click==8.1.7 click==8.1.7
cryptography==41.0.7 cryptography==41.0.7
dictdiffer==0.9.0
dnspython==2.4.2
ecdsa==0.18.0 ecdsa==0.18.0
email-validator==2.1.0.post1
exceptiongroup==1.2.0 exceptiongroup==1.2.0
fastapi==0.104.1 fastapi==0.104.1
h11==0.14.0 h11==0.14.0
...@@ -29,6 +33,7 @@ rsa==4.9 ...@@ -29,6 +33,7 @@ rsa==4.9
six==1.16.0 six==1.16.0
sniffio==1.3.0 sniffio==1.3.0
starlette==0.27.0 starlette==0.27.0
tomlkit==0.12.3
tortoise-orm==0.20.0 tortoise-orm==0.20.0
typing_extensions==4.8.0 typing_extensions==4.8.0
uvicorn==0.24.0.post1 uvicorn==0.24.0.post1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment