added create product

parent 952e9c9c
...@@ -131,3 +131,4 @@ dmypy.json ...@@ -131,3 +131,4 @@ dmypy.json
data/ data/
.idea .idea
minio/
\ No newline at end of file
...@@ -9,20 +9,20 @@ services: ...@@ -9,20 +9,20 @@ services:
restart: always restart: always
ports: ports:
- "8000:8000" - "8000:8000"
environment: env_file:
- database_url=postgres://postgres:postgres@db:5432/postgres - .env
networks: networks:
- fastfoodnetwork - fastfoodnetwork
volumes: volumes:
- .:/app - .:/app
command: bash -c "poetry run python3 src/app/main.py" command: bash -c "poetry run python3 src/app/main.py"
depends_on: depends_on:
- db - postgres
db: postgres:
image: postgres:14-alpine image: postgres:14-alpine
environment: env_file:
- POSTGRES_PASSWORD=postgres - .env
ports: ports:
- "5440:5432" - "5440:5432"
volumes: volumes:
...@@ -30,6 +30,23 @@ services: ...@@ -30,6 +30,23 @@ services:
networks: networks:
- fastfoodnetwork - fastfoodnetwork
minio:
image: minio/minio
ports:
- "9000:9000"
- "9090:9090"
env_file:
- .env
command: server --console-address ":9090" /data
volumes:
- ./minio/data:/data
- ./minio/conf:/root/.minio
depends_on:
- postgres
- core
networks:
- fastfoodnetwork
networks: networks:
fastfoodnetwork: fastfoodnetwork:
driver: bridge driver: bridge
This diff is collapsed.
...@@ -12,8 +12,15 @@ uvicorn = "^0.24.0.post1" ...@@ -12,8 +12,15 @@ uvicorn = "^0.24.0.post1"
tortoise-orm = {extras = ["asyncpg"], version = "^0.20.0"} tortoise-orm = {extras = ["asyncpg"], version = "^0.20.0"}
aerich = "^0.7.2" aerich = "^0.7.2"
asyncpg = "^0.29.0" asyncpg = "^0.29.0"
python-multipart = "^0.0.6"
miniopy-async = "^1.17"
[tool.aerich]
tortoise_orm = "src.app.db.conf.TORTOISE_ORM"
location = "src/migrations"
src_folder = "./."
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
import io
import fastapi
from miniopy_async import Minio
import settings
class MinioClient(Minio):
def __init__(self, *args, **kwargs):
super().__init__(
*args,
endpoint=settings.minio_url,
access_key=settings.minio_root_user,
secret_key=settings.minio_root_password,
secure=False,
**kwargs,
)
async def upload_from_bytes(self, file: fastapi.UploadFile):
file_data = await file.read()
return await self.put_object(
bucket_name=settings.minio_bucket_name,
object_name=file.filename,
data=io.BytesIO(file_data),
length=len(file_data),
)
from tortoise.exceptions import ValidationError
from ..base import BaseController
from .services import ProductService
from exceptions import common as common_exc, http as http_exc
class ProductController(BaseController):
product_service = ProductService()
async def create(self, **kwargs):
try:
return await self.product_service.create_product(**kwargs)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except ValidationError as e:
raise http_exc.HTTPBadRequestException(detail=f"Validation error: {str(e)}")
import fastapi as fa
from .ctrl import ProductController
router = fa.APIRouter(prefix='/products', tags=['Product'])
ctrl = ProductController()
@router.post('')
async def create_product(
image: fa.UploadFile,
name: str = fa.Form(...),
description: str = fa.Form(None),
price: float = fa.Form(...)
):
return await ctrl.create(name=name, description=description, price=price, image=image)
from uuid import UUID
from pydantic import BaseModel, ConfigDict
class ProductBaseSchema(BaseModel):
name: str | None = None
class ProductIdSchema(BaseModel):
id: UUID
model_config = ConfigDict(from_attributes=True)
class ProductListSchema(ProductBaseSchema, ProductIdSchema):
pass
class ProductGetSchema(ProductBaseSchema, ProductIdSchema):
description: str | None = None
price: float | None = None
image: str | None = None
from db.repositories.product import ProductRepository
from .schemas import ProductGetSchema
from adapters import MinioClient
import settings
class ProductService:
product_repository = ProductRepository()
client = MinioClient()
async def create_product(self, **kwargs):
image = kwargs.get('image')
await self.client.upload_from_bytes(file=image)
product = await self.product_repository.create(
name=kwargs.get('name'),
description=kwargs.get('description'),
price=kwargs.get('price'),
image=f"minio://{settings.minio_bucket_name}/{image.filename}",
)
return ProductGetSchema.model_validate(product)
from fastapi import APIRouter from fastapi import APIRouter
from .product.routes import router as product_route
router = APIRouter(prefix='/api') router = APIRouter(prefix='/api')
router.include_router(product_route)
...@@ -3,8 +3,9 @@ import os ...@@ -3,8 +3,9 @@ import os
from fastapi import FastAPI from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.fastapi import register_tortoise
TORTOISE_ORM = { TORTOISE_ORM = {
'connections': {'default': os.environ.get('database_url')}, 'connections': {'default': os.environ.get('DATABASE_URL')},
'apps': { 'apps': {
'models': { 'models': {
'models': ['src.app.db.models', 'aerich.models'], 'models': ['src.app.db.models', 'aerich.models'],
...@@ -17,7 +18,7 @@ TORTOISE_ORM = { ...@@ -17,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=os.environ.get('database_url'), db_url=os.environ.get('DATABASE_URL'),
modules={"models": ["db.models"]}, modules={"models": ["db.models"]},
generate_schemas=False, generate_schemas=False,
add_exception_handlers=True, add_exception_handlers=True,
......
from tortoise import fields
from .base import BaseModel
class Product(BaseModel):
name = fields.CharField(max_length=255)
description = fields.TextField(null=True, default=None)
price = fields.FloatField()
image = fields.TextField()
def __str__(self):
return self.name
class Meta(BaseModel.Meta):
table = 'products'
from .base import BaseRepository
from ..models import Product
class ProductRepository(BaseRepository):
model = Product
...@@ -3,4 +3,9 @@ import os ...@@ -3,4 +3,9 @@ import os
import pydantic import pydantic
database_url = pydantic.PostgresDsn(os.getenv('database_url')) database_url = pydantic.PostgresDsn(os.getenv('DATABASE_URL'))
minio_root_user = os.getenv('MINIO_ROOT_USER')
minio_root_password = os.getenv('MINIO_ROOT_PASSWORD')
minio_url = os.getenv('MINIO_URL')
minio_bucket_name = os.getenv('MINIO_BUCKET_NAME')
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
CREATE TABLE IF NOT EXISTS "basemodel" (
"id" UUID NOT NULL PRIMARY KEY,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "products" (
"id" UUID NOT NULL PRIMARY KEY,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"name" VARCHAR(255) NOT NULL,
"description" TEXT NOT NULL,
"price" DOUBLE PRECISION NOT NULL,
"image" TEXT 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 "products" ALTER COLUMN "description" DROP NOT NULL;"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "products" ALTER COLUMN "description" SET NOT NULL;"""
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