Complete e-commerce API implementation with all required endpoints

This commit is contained in:
Automated Action 2025-05-26 15:12:01 +00:00
parent fd7eca550f
commit c6db3ebe3b
17 changed files with 1175 additions and 14 deletions

145
app/api/endpoints/cart.py Normal file
View File

@ -0,0 +1,145 @@
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app.api import deps
router = APIRouter()
@router.get("/", response_model=schemas.Cart)
def read_user_cart(
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get current user's cart with all items.
"""
cart = crud.cart.get_cart_with_items(db, user_id=current_user.id)
if not cart:
# Create a new cart for the user if it doesn't exist
cart = crud.cart.create(db, obj_in=schemas.CartCreate(user_id=current_user.id))
return cart
@router.post("/items", response_model=schemas.CartItem)
def add_cart_item(
*,
db: Session = Depends(deps.get_db),
item_in: schemas.CartItemCreate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Add item to cart. If item already exists, update quantity.
"""
# Verify the product exists and is active
product = crud.product.get(db, id=item_in.product_id)
if not product or not product.is_active:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found or inactive",
)
# Check if product is in stock
if product.stock < item_in.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock. Available: {product.stock}",
)
# Get or create user's cart
cart = crud.cart.get_or_create_cart(db, user_id=current_user.id)
# Add or update cart item
cart_item = crud.cart_item.create_or_update_cart_item(
db, cart_id=cart.id, product_id=item_in.product_id, quantity=item_in.quantity
)
return cart_item
@router.put("/items/{product_id}", response_model=schemas.CartItem)
def update_cart_item(
*,
db: Session = Depends(deps.get_db),
product_id: int,
item_in: schemas.CartItemUpdate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update cart item quantity.
"""
# Verify the product exists and is active
product = crud.product.get(db, id=product_id)
if not product or not product.is_active:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found or inactive",
)
# Check if product is in stock
if product.stock < item_in.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock. Available: {product.stock}",
)
# Get user's cart
cart = crud.cart.get_by_user_id(db, user_id=current_user.id)
if not cart:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cart not found",
)
# Update cart item
cart_item = crud.cart_item.create_or_update_cart_item(
db, cart_id=cart.id, product_id=product_id, quantity=item_in.quantity
)
return cart_item
@router.delete("/items/{product_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def remove_cart_item(
*,
db: Session = Depends(deps.get_db),
product_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
) -> None:
"""
Remove item from cart.
"""
# Get user's cart
cart = crud.cart.get_by_user_id(db, user_id=current_user.id)
if not cart:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cart not found",
)
# Remove cart item
crud.cart_item.remove_cart_item(db, cart_id=cart.id, product_id=product_id)
@router.delete("/", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def clear_cart(
*,
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user),
) -> None:
"""
Clear all items from cart.
"""
# Get user's cart
cart = crud.cart.get_by_user_id(db, user_id=current_user.id)
if not cart:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cart not found",
)
# Clear cart
crud.cart_item.clear_cart(db, cart_id=cart.id)

View File

@ -0,0 +1,110 @@
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app.api import deps
router = APIRouter()
@router.get("/", response_model=List[schemas.Category])
def read_categories(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
) -> Any:
"""
Retrieve categories.
"""
categories = crud.category.get_multi(db, skip=skip, limit=limit)
return categories
@router.post("/", response_model=schemas.Category)
def create_category(
*,
db: Session = Depends(deps.get_db),
category_in: schemas.CategoryCreate,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> Any:
"""
Create new category (admin only).
"""
category = crud.category.get_by_name(db, name=category_in.name)
if category:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Category with this name already exists",
)
category = crud.category.create(db, obj_in=category_in)
return category
@router.get("/{id}", response_model=schemas.Category)
def read_category(
*,
db: Session = Depends(deps.get_db),
id: int,
) -> Any:
"""
Get category by ID.
"""
category = crud.category.get(db, id=id)
if not category:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Category not found",
)
return category
@router.put("/{id}", response_model=schemas.Category)
def update_category(
*,
db: Session = Depends(deps.get_db),
id: int,
category_in: schemas.CategoryUpdate,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> Any:
"""
Update a category (admin only).
"""
category = crud.category.get(db, id=id)
if not category:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Category not found",
)
# Check if name already exists (if updating name)
if category_in.name and category_in.name != category.name:
existing_category = crud.category.get_by_name(db, name=category_in.name)
if existing_category:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Category with this name already exists",
)
category = crud.category.update(db, db_obj=category, obj_in=category_in)
return category
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def delete_category(
*,
db: Session = Depends(deps.get_db),
id: int,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> None:
"""
Delete a category (admin only).
"""
category = crud.category.get(db, id=id)
if not category:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Category not found",
)
crud.category.remove(db, id=id)

172
app/api/endpoints/orders.py Normal file
View File

@ -0,0 +1,172 @@
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app.api import deps
router = APIRouter()
@router.get("/", response_model=List[schemas.Order])
def read_user_orders(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Retrieve current user's orders.
"""
orders = crud.order.get_orders_by_user(
db, user_id=current_user.id, skip=skip, limit=limit
)
return orders
@router.post("/", response_model=schemas.Order)
def create_order(
*,
db: Session = Depends(deps.get_db),
order_in: schemas.OrderCreate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Create a new order.
"""
# Validate order items
if not order_in.items or len(order_in.items) == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Order must contain at least one item",
)
# Check if products exist and are in stock
for item in order_in.items:
product = crud.product.get(db, id=item.product_id)
if not product or not product.is_active:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Product with id {item.product_id} not found or inactive",
)
if product.stock < item.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock for product {product.name}. Available: {product.stock}",
)
# Create order
order = crud.order.create_with_items(
db, obj_in=order_in, user_id=current_user.id
)
# Update product stock
for item in order.items:
product = crud.product.get(db, id=item.product_id)
product.stock -= item.quantity
db.add(product)
# Clear user's cart
cart = crud.cart.get_by_user_id(db, user_id=current_user.id)
if cart:
crud.cart_item.clear_cart(db, cart_id=cart.id)
db.commit()
return order
@router.get("/{order_id}", response_model=schemas.Order)
def read_order(
*,
db: Session = Depends(deps.get_db),
order_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get order by ID.
"""
order = crud.order.get_order_with_items(db, order_id=order_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
# Check if user is authorized to access this order
if order.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
return order
@router.put("/{order_id}", response_model=schemas.Order)
def update_order_status(
*,
db: Session = Depends(deps.get_db),
order_id: int,
order_in: schemas.OrderUpdate,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> Any:
"""
Update order status (admin only).
"""
order = crud.order.get(db, id=order_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
order = crud.order.update(db, db_obj=order, obj_in=order_in)
return order
@router.delete("/{order_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def cancel_order(
*,
db: Session = Depends(deps.get_db),
order_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
) -> None:
"""
Cancel an order. Only allowed for pending orders.
"""
order = crud.order.get_order_with_items(db, order_id=order_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
# Check if user is authorized to cancel this order
if order.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
# Check if order can be cancelled
if order.status != models.OrderStatus.PENDING:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Only pending orders can be cancelled",
)
# Update order status to cancelled
order.status = models.OrderStatus.CANCELLED
# Restore product stock
for item in order.items:
product = crud.product.get(db, id=item.product_id)
if product:
product.stock += item.quantity
db.add(product)
db.add(order)
db.commit()

View File

@ -0,0 +1,108 @@
from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app.api import deps
router = APIRouter()
@router.get("/", response_model=List[schemas.Product])
def read_products(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
name: Optional[str] = None,
category_id: Optional[int] = None,
min_price: Optional[float] = None,
max_price: Optional[float] = None,
in_stock: Optional[bool] = None,
) -> Any:
"""
Retrieve products with filtering options.
"""
filter_params = schemas.ProductFilterParams(
name=name,
category_id=category_id,
min_price=min_price,
max_price=max_price,
in_stock=in_stock,
)
products = crud.product.search_products(
db, filter_params=filter_params, skip=skip, limit=limit
)
return products
@router.post("/", response_model=schemas.Product)
def create_product(
*,
db: Session = Depends(deps.get_db),
product_in: schemas.ProductCreate,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> Any:
"""
Create new product (admin only).
"""
product = crud.product.create(db, obj_in=product_in)
return product
@router.get("/{id}", response_model=schemas.Product)
def read_product(
*,
db: Session = Depends(deps.get_db),
id: int,
) -> Any:
"""
Get product by ID.
"""
product = crud.product.get(db, id=id)
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found",
)
return product
@router.put("/{id}", response_model=schemas.Product)
def update_product(
*,
db: Session = Depends(deps.get_db),
id: int,
product_in: schemas.ProductUpdate,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> Any:
"""
Update a product (admin only).
"""
product = crud.product.get(db, id=id)
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found",
)
product = crud.product.update(db, db_obj=product, obj_in=product_in)
return product
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def delete_product(
*,
db: Session = Depends(deps.get_db),
id: int,
current_user: models.User = Depends(deps.get_current_admin_user),
) -> None:
"""
Delete a product (admin only).
"""
product = crud.product.get(db, id=id)
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found",
)
crud.product.remove(db, id=id)

View File

@ -0,0 +1,124 @@
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import crud, models, schemas
from app.api import deps
router = APIRouter()
@router.get("/product/{product_id}", response_model=List[schemas.Review])
def read_product_reviews(
*,
db: Session = Depends(deps.get_db),
product_id: int,
skip: int = 0,
limit: int = 100,
) -> Any:
"""
Retrieve reviews for a specific product.
"""
# Check if product exists
product = crud.product.get(db, id=product_id)
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found",
)
reviews = crud.review.get_reviews_by_product(
db, product_id=product_id, skip=skip, limit=limit
)
return reviews
@router.post("/", response_model=schemas.Review)
def create_review(
*,
db: Session = Depends(deps.get_db),
review_in: schemas.ReviewCreate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Create a new review for a product.
"""
# Check if product exists
product = crud.product.get(db, id=review_in.product_id)
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found",
)
# Check if user has already reviewed this product
existing_review = crud.review.get_user_review_for_product(
db, user_id=current_user.id, product_id=review_in.product_id
)
if existing_review:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="You have already reviewed this product",
)
review = crud.review.create_user_review(
db, obj_in=review_in, user_id=current_user.id
)
return review
@router.put("/{review_id}", response_model=schemas.Review)
def update_review(
*,
db: Session = Depends(deps.get_db),
review_id: int,
review_in: schemas.ReviewUpdate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Update a review.
"""
review = crud.review.get(db, id=review_id)
if not review:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Review not found",
)
# Check if user is authorized to update this review
if review.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
review = crud.review.update(db, db_obj=review, obj_in=review_in)
return review
@router.delete("/{review_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def delete_review(
*,
db: Session = Depends(deps.get_db),
review_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
) -> None:
"""
Delete a review.
"""
review = crud.review.get(db, id=review_id)
if not review:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Review not found",
)
# Check if user is authorized to delete this review
if review.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
crud.review.remove(db, id=review_id)

View File

@ -1,9 +1,14 @@
from fastapi import APIRouter
from app.api.endpoints import auth, users
from app.api.endpoints import auth, cart, categories, orders, products, reviews, users
api_router = APIRouter()
# Include authentication and user endpoints
# Include all API endpoints
api_router.include_router(auth.router, prefix="/auth", tags=["Authentication"])
api_router.include_router(users.router, prefix="/users", tags=["Users"])
api_router.include_router(products.router, prefix="/products", tags=["Products"])
api_router.include_router(categories.router, prefix="/categories", tags=["Categories"])
api_router.include_router(cart.router, prefix="/cart", tags=["Shopping Cart"])
api_router.include_router(orders.router, prefix="/orders", tags=["Orders"])
api_router.include_router(reviews.router, prefix="/reviews", tags=["Reviews"])

View File

@ -1,3 +1,7 @@
from app.crud.crud_cart import cart, cart_item
from app.crud.crud_order import order, order_item
from app.crud.crud_product import category, product
from app.crud.crud_review import review
from app.crud.crud_user import user
__all__ = ["user"]
__all__ = ["user", "product", "category", "cart", "cart_item", "order", "order_item", "review"]

65
app/crud/crud_cart.py Normal file
View File

@ -0,0 +1,65 @@
from typing import Optional
from sqlalchemy.orm import Session, joinedload
from app.crud.base import CRUDBase
from app.models.cart import Cart, CartItem
from app.schemas.cart import CartCreate, CartItemCreate, CartItemUpdate
class CRUDCart(CRUDBase[Cart, CartCreate, CartCreate]):
def get_by_user_id(self, db: Session, *, user_id: int) -> Optional[Cart]:
return db.query(Cart).filter(Cart.user_id == user_id).first()
def get_or_create_cart(self, db: Session, *, user_id: int) -> Cart:
cart = self.get_by_user_id(db, user_id=user_id)
if not cart:
cart = self.create(db, obj_in=CartCreate(user_id=user_id))
return cart
def get_cart_with_items(self, db: Session, *, user_id: int) -> Optional[Cart]:
return (
db.query(Cart)
.filter(Cart.user_id == user_id)
.options(joinedload(Cart.items).joinedload(CartItem.product))
.first()
)
class CRUDCartItem(CRUDBase[CartItem, CartItemCreate, CartItemUpdate]):
def create_or_update_cart_item(
self, db: Session, *, cart_id: int, product_id: int, quantity: int
) -> CartItem:
cart_item = (
db.query(CartItem)
.filter(CartItem.cart_id == cart_id, CartItem.product_id == product_id)
.first()
)
if cart_item:
cart_item.quantity = quantity
db.add(cart_item)
db.commit()
db.refresh(cart_item)
return cart_item
else:
return self.create(
db,
obj_in=CartItemCreate(cart_id=cart_id, product_id=product_id, quantity=quantity)
)
def remove_cart_item(
self, db: Session, *, cart_id: int, product_id: int
) -> None:
db.query(CartItem).filter(
CartItem.cart_id == cart_id, CartItem.product_id == product_id
).delete()
db.commit()
def clear_cart(self, db: Session, *, cart_id: int) -> None:
db.query(CartItem).filter(CartItem.cart_id == cart_id).delete()
db.commit()
cart = CRUDCart(Cart)
cart_item = CRUDCartItem(CartItem)

69
app/crud/crud_order.py Normal file
View File

@ -0,0 +1,69 @@
from typing import List, Optional
from sqlalchemy.orm import Session, joinedload
from app.crud.base import CRUDBase
from app.models.order import Order, OrderItem
from app.schemas.order import OrderCreate, OrderItemCreate, OrderUpdate
class CRUDOrder(CRUDBase[Order, OrderCreate, OrderUpdate]):
def get_orders_by_user(
self, db: Session, *, user_id: int, skip: int = 0, limit: int = 100
) -> List[Order]:
return (
db.query(Order)
.filter(Order.user_id == user_id)
.order_by(Order.created_at.desc())
.offset(skip)
.limit(limit)
.all()
)
def get_order_with_items(self, db: Session, *, order_id: int) -> Optional[Order]:
return (
db.query(Order)
.filter(Order.id == order_id)
.options(joinedload(Order.items))
.first()
)
def create_with_items(
self, db: Session, *, obj_in: OrderCreate, user_id: int
) -> Order:
# Calculate the total amount
total_amount = sum(item.unit_price * item.quantity for item in obj_in.items)
# Create order
db_obj = Order(
user_id=user_id,
status=obj_in.status,
total_amount=total_amount,
shipping_address=obj_in.shipping_address,
payment_details=obj_in.payment_details,
tracking_number=obj_in.tracking_number,
)
db.add(db_obj)
db.flush()
# Create order items
for item in obj_in.items:
order_item = OrderItem(
order_id=db_obj.id,
product_id=item.product_id,
quantity=item.quantity,
unit_price=item.unit_price,
)
db.add(order_item)
db.commit()
db.refresh(db_obj)
return db_obj
class CRUDOrderItem(CRUDBase[OrderItem, OrderItemCreate, OrderItemCreate]):
pass
order = CRUDOrder(Order)
order_item = CRUDOrderItem(OrderItem)

53
app/crud/crud_product.py Normal file
View File

@ -0,0 +1,53 @@
from typing import List, Optional
from sqlalchemy.orm import Session
from app.crud.base import CRUDBase
from app.models.product import Category, Product
from app.schemas.product import ProductCreate, ProductFilterParams, ProductUpdate
class CRUDProduct(CRUDBase[Product, ProductCreate, ProductUpdate]):
def search_products(
self, db: Session, *, filter_params: ProductFilterParams, skip: int = 0, limit: int = 100
) -> List[Product]:
query = db.query(Product)
# Apply filters
if filter_params.name:
query = query.filter(Product.name.ilike(f"%{filter_params.name}%"))
if filter_params.category_id:
query = query.filter(Product.category_id == filter_params.category_id)
if filter_params.min_price is not None:
query = query.filter(Product.price >= filter_params.min_price)
if filter_params.max_price is not None:
query = query.filter(Product.price <= filter_params.max_price)
if filter_params.in_stock is not None:
if filter_params.in_stock:
query = query.filter(Product.stock > 0)
else:
query = query.filter(Product.stock == 0)
# Only return active products by default
query = query.filter(Product.is_active)
# Order by newest first
query = query.order_by(Product.created_at.desc())
return query.offset(skip).limit(limit).all()
def get_product_with_category(self, db: Session, product_id: int) -> Optional[Product]:
return db.query(Product).filter(Product.id == product_id).first()
class CRUDCategory(CRUDBase[Category, ProductCreate, ProductUpdate]):
def get_by_name(self, db: Session, *, name: str) -> Optional[Category]:
return db.query(Category).filter(Category.name == name).first()
product = CRUDProduct(Product)
category = CRUDCategory(Category)

59
app/crud/crud_review.py Normal file
View File

@ -0,0 +1,59 @@
from typing import List, Optional
from sqlalchemy.orm import Session
from app.crud.base import CRUDBase
from app.models.review import Review
from app.schemas.review import ReviewCreate, ReviewUpdate
class CRUDReview(CRUDBase[Review, ReviewCreate, ReviewUpdate]):
def get_reviews_by_product(
self, db: Session, *, product_id: int, skip: int = 0, limit: int = 100
) -> List[Review]:
return (
db.query(Review)
.filter(Review.product_id == product_id)
.order_by(Review.created_at.desc())
.offset(skip)
.limit(limit)
.all()
)
def get_reviews_by_user(
self, db: Session, *, user_id: int, skip: int = 0, limit: int = 100
) -> List[Review]:
return (
db.query(Review)
.filter(Review.user_id == user_id)
.order_by(Review.created_at.desc())
.offset(skip)
.limit(limit)
.all()
)
def get_user_review_for_product(
self, db: Session, *, user_id: int, product_id: int
) -> Optional[Review]:
return (
db.query(Review)
.filter(Review.user_id == user_id, Review.product_id == product_id)
.first()
)
def create_user_review(
self, db: Session, *, obj_in: ReviewCreate, user_id: int
) -> Review:
db_obj = Review(
user_id=user_id,
product_id=obj_in.product_id,
rating=obj_in.rating,
comment=obj_in.comment,
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
review = CRUDReview(Review)

View File

@ -1,5 +1,25 @@
# Import schemas for convenience
from app.schemas.cart import Cart, CartCreate, CartItem, CartItemCreate, CartItemUpdate
from app.schemas.order import Order, OrderCreate, OrderItem, OrderItemCreate, OrderUpdate
from app.schemas.product import (
Category,
CategoryCreate,
CategoryUpdate,
Product,
ProductCreate,
ProductFilterParams,
ProductUpdate,
)
from app.schemas.review import Review, ReviewCreate, ReviewUpdate
from app.schemas.token import Token, TokenPayload
from app.schemas.user import User, UserCreate, UserInDB, UserUpdate
__all__ = ["Token", "TokenPayload", "User", "UserCreate", "UserInDB", "UserUpdate"]
__all__ = [
"Token", "TokenPayload",
"User", "UserCreate", "UserInDB", "UserUpdate",
"Product", "ProductCreate", "ProductUpdate",
"Category", "CategoryCreate", "CategoryUpdate", "ProductFilterParams",
"Order", "OrderCreate", "OrderUpdate", "OrderItem", "OrderItemCreate",
"Cart", "CartCreate", "CartItem", "CartItemCreate", "CartItemUpdate",
"Review", "ReviewCreate", "ReviewUpdate"
]

56
app/schemas/cart.py Normal file
View File

@ -0,0 +1,56 @@
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, Field
from app.schemas.product import Product
# Cart Item schemas
class CartItemBase(BaseModel):
product_id: int
quantity: int = Field(1, gt=0)
class CartItemCreate(CartItemBase):
pass
class CartItemUpdate(BaseModel):
quantity: int = Field(..., gt=0)
class CartItemInDBBase(CartItemBase):
id: int
cart_id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class CartItem(CartItemInDBBase):
product: Optional[Product] = None
# Cart schemas
class CartBase(BaseModel):
user_id: int
class CartCreate(CartBase):
pass
class CartInDBBase(CartBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Cart(CartInDBBase):
items: List[CartItem] = []

64
app/schemas/order.py Normal file
View File

@ -0,0 +1,64 @@
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, Field
from app.models.order import OrderStatus
# Order Item schemas
class OrderItemBase(BaseModel):
product_id: int
quantity: int = Field(..., gt=0)
unit_price: float = Field(..., gt=0)
class OrderItemCreate(OrderItemBase):
pass
class OrderItemInDBBase(OrderItemBase):
id: int
order_id: int
created_at: datetime
class Config:
from_attributes = True
class OrderItem(OrderItemInDBBase):
pass
# Order schemas
class OrderBase(BaseModel):
status: OrderStatus = OrderStatus.PENDING
shipping_address: str
payment_details: Optional[str] = None
tracking_number: Optional[str] = None
class OrderCreate(OrderBase):
items: List[OrderItemCreate]
class OrderUpdate(BaseModel):
status: Optional[OrderStatus] = None
shipping_address: Optional[str] = None
payment_details: Optional[str] = None
tracking_number: Optional[str] = None
class OrderInDBBase(OrderBase):
id: int
user_id: int
total_amount: float
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Order(OrderInDBBase):
items: List[OrderItem]

75
app/schemas/product.py Normal file
View File

@ -0,0 +1,75 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
# Category schemas
class CategoryBase(BaseModel):
name: str
description: Optional[str] = None
class CategoryCreate(CategoryBase):
pass
class CategoryUpdate(CategoryBase):
name: Optional[str] = None
class CategoryInDBBase(CategoryBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Category(CategoryInDBBase):
pass
# Product schemas
class ProductBase(BaseModel):
name: str
description: Optional[str] = None
price: float = Field(..., gt=0)
stock: int = Field(0, ge=0)
image_url: Optional[str] = None
is_active: bool = True
category_id: Optional[int] = None
class ProductCreate(ProductBase):
pass
class ProductUpdate(ProductBase):
name: Optional[str] = None
price: Optional[float] = Field(None, gt=0)
stock: Optional[int] = Field(None, ge=0)
category_id: Optional[int] = None
class ProductInDBBase(ProductBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Product(ProductInDBBase):
category: Optional[Category] = None
# Product search and filtering
class ProductFilterParams(BaseModel):
name: Optional[str] = None
category_id: Optional[int] = None
min_price: Optional[float] = None
max_price: Optional[float] = None
in_stock: Optional[bool] = None

33
app/schemas/review.py Normal file
View File

@ -0,0 +1,33 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
class ReviewBase(BaseModel):
product_id: int
rating: float = Field(..., ge=1, le=5)
comment: Optional[str] = None
class ReviewCreate(ReviewBase):
pass
class ReviewUpdate(BaseModel):
rating: Optional[float] = Field(None, ge=1, le=5)
comment: Optional[str] = None
class ReviewInDBBase(ReviewBase):
id: int
user_id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Review(ReviewInDBBase):
pass

View File

@ -5,9 +5,8 @@ Revises:
Create Date: 2023-10-30 00:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '9a4f22e84e75'
@ -149,29 +148,29 @@ def downgrade() -> None:
# Drop all tables in reverse order
op.drop_index(op.f('ix_reviews_id'), table_name='reviews')
op.drop_table('reviews')
op.drop_index(op.f('ix_order_items_id'), table_name='order_items')
op.drop_table('order_items')
op.drop_index(op.f('ix_orders_id'), table_name='orders')
op.drop_table('orders')
op.drop_index(op.f('ix_cart_items_id'), table_name='cart_items')
op.drop_table('cart_items')
op.drop_index(op.f('ix_carts_id'), table_name='carts')
op.drop_table('carts')
op.drop_index(op.f('ix_products_name'), table_name='products')
op.drop_index(op.f('ix_products_id'), table_name='products')
op.drop_table('products')
op.drop_index(op.f('ix_categories_name'), table_name='categories')
op.drop_index(op.f('ix_categories_id'), table_name='categories')
op.drop_table('categories')
op.drop_index(op.f('ix_users_full_name'), table_name='users')
op.drop_index(op.f('ix_users_username'), table_name='users')
op.drop_index(op.f('ix_users_email'), table_name='users')
op.drop_index(op.f('ix_users_id'), table_name='users')
op.drop_table('users')
op.drop_table('users')