Add database models for food delivery API
- Create base model with timestamp mixin - Add user model with roles - Add restaurant model - Add menu item model with categories - Add order and order item models - Add delivery model - Fix import issues and linting errors
This commit is contained in:
parent
a0217b10ac
commit
5570e6e49e
@ -0,0 +1,19 @@
|
||||
# Import all models for easier access
|
||||
# These imports are intentional for making models available from this module
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin # noqa: F401
|
||||
from app.models.user import User, UserRole # noqa: F401
|
||||
from app.models.restaurant import Restaurant # noqa: F401
|
||||
from app.models.menu_item import MenuItem, MenuItemCategory # noqa: F401
|
||||
from app.models.order import Order, OrderItem, OrderStatus, PaymentMethod, PaymentStatus # noqa: F401
|
||||
from app.models.delivery import Delivery, DeliveryStatus # noqa: F401
|
||||
|
||||
# Define __all__ to explicitly export these names
|
||||
__all__ = [
|
||||
"BaseModel", "TimestampMixin",
|
||||
"User", "UserRole",
|
||||
"Restaurant",
|
||||
"MenuItem", "MenuItemCategory",
|
||||
"Order", "OrderItem", "OrderStatus", "PaymentMethod", "PaymentStatus",
|
||||
"Delivery", "DeliveryStatus",
|
||||
]
|
25
app/models/base.py
Normal file
25
app/models/base.py
Normal file
@ -0,0 +1,25 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, Integer
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
|
||||
from app.db.session import Base
|
||||
|
||||
|
||||
class TimestampMixin:
|
||||
"""Mixin that adds created_at and updated_at columns to models."""
|
||||
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
|
||||
class BaseModel(Base):
|
||||
"""Base model for all models to inherit from."""
|
||||
|
||||
__abstract__ = True
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
@declared_attr
|
||||
def __tablename__(cls):
|
||||
return cls.__name__.lower()
|
36
app/models/delivery.py
Normal file
36
app/models/delivery.py
Normal file
@ -0,0 +1,36 @@
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import Column, DateTime, Enum as SQLEnum, Float, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin
|
||||
|
||||
|
||||
class DeliveryStatus(str, Enum):
|
||||
"""Enum for delivery statuses."""
|
||||
|
||||
PENDING = "pending"
|
||||
ASSIGNED = "assigned"
|
||||
PICKED_UP = "picked_up"
|
||||
IN_TRANSIT = "in_transit"
|
||||
DELIVERED = "delivered"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class Delivery(BaseModel, TimestampMixin):
|
||||
"""Delivery model."""
|
||||
|
||||
driver_id = Column(Integer, ForeignKey("user.id"), nullable=True)
|
||||
status = Column(SQLEnum(DeliveryStatus), default=DeliveryStatus.PENDING, nullable=False)
|
||||
pickup_time = Column(DateTime, nullable=True)
|
||||
delivery_time = Column(DateTime, nullable=True)
|
||||
current_latitude = Column(Float, nullable=True)
|
||||
current_longitude = Column(Float, nullable=True)
|
||||
notes = Column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
driver = relationship("User", foreign_keys=[driver_id], backref="deliveries")
|
||||
order = relationship("Order", back_populates="delivery", uselist=False)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Delivery {self.id}>"
|
39
app/models/menu_item.py
Normal file
39
app/models/menu_item.py
Normal file
@ -0,0 +1,39 @@
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import Boolean, Column, Enum as SQLEnum, Float, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin
|
||||
|
||||
|
||||
class MenuItemCategory(str, Enum):
|
||||
"""Enum for menu item categories."""
|
||||
|
||||
APPETIZER = "appetizer"
|
||||
MAIN_COURSE = "main_course"
|
||||
DESSERT = "dessert"
|
||||
BEVERAGE = "beverage"
|
||||
SIDE = "side"
|
||||
SPECIAL = "special"
|
||||
|
||||
|
||||
class MenuItem(BaseModel, TimestampMixin):
|
||||
"""Menu item model."""
|
||||
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
price = Column(Float, nullable=False)
|
||||
image_url = Column(String, nullable=True)
|
||||
category = Column(SQLEnum(MenuItemCategory), nullable=False)
|
||||
is_vegetarian = Column(Boolean, default=False, nullable=False)
|
||||
is_vegan = Column(Boolean, default=False, nullable=False)
|
||||
is_gluten_free = Column(Boolean, default=False, nullable=False)
|
||||
is_available = Column(Boolean, default=True, nullable=False)
|
||||
restaurant_id = Column(Integer, ForeignKey("restaurant.id"), nullable=False)
|
||||
|
||||
# Relationships
|
||||
restaurant = relationship("Restaurant", back_populates="menu_items")
|
||||
order_items = relationship("OrderItem", back_populates="menu_item")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<MenuItem {self.name}>"
|
78
app/models/order.py
Normal file
78
app/models/order.py
Normal file
@ -0,0 +1,78 @@
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import Column, DateTime, Enum as SQLEnum, Float, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin
|
||||
|
||||
|
||||
class OrderStatus(str, Enum):
|
||||
"""Enum for order statuses."""
|
||||
|
||||
PENDING = "pending"
|
||||
CONFIRMED = "confirmed"
|
||||
PREPARING = "preparing"
|
||||
READY_FOR_PICKUP = "ready_for_pickup"
|
||||
OUT_FOR_DELIVERY = "out_for_delivery"
|
||||
DELIVERED = "delivered"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class PaymentMethod(str, Enum):
|
||||
"""Enum for payment methods."""
|
||||
|
||||
CREDIT_CARD = "credit_card"
|
||||
DEBIT_CARD = "debit_card"
|
||||
CASH = "cash"
|
||||
DIGITAL_WALLET = "digital_wallet"
|
||||
|
||||
|
||||
class PaymentStatus(str, Enum):
|
||||
"""Enum for payment statuses."""
|
||||
|
||||
PENDING = "pending"
|
||||
PAID = "paid"
|
||||
FAILED = "failed"
|
||||
REFUNDED = "refunded"
|
||||
|
||||
|
||||
class Order(BaseModel, TimestampMixin):
|
||||
"""Order model."""
|
||||
|
||||
customer_id = Column(Integer, ForeignKey("user.id"), nullable=False)
|
||||
restaurant_id = Column(Integer, ForeignKey("restaurant.id"), nullable=False)
|
||||
delivery_id = Column(Integer, ForeignKey("delivery.id"), nullable=True)
|
||||
status = Column(SQLEnum(OrderStatus), default=OrderStatus.PENDING, nullable=False)
|
||||
total_amount = Column(Float, nullable=False)
|
||||
delivery_address = Column(String, nullable=False)
|
||||
delivery_notes = Column(Text, nullable=True)
|
||||
payment_method = Column(SQLEnum(PaymentMethod), nullable=False)
|
||||
payment_status = Column(SQLEnum(PaymentStatus), default=PaymentStatus.PENDING, nullable=False)
|
||||
estimated_delivery_time = Column(DateTime, nullable=True)
|
||||
actual_delivery_time = Column(DateTime, nullable=True)
|
||||
|
||||
# Relationships
|
||||
customer = relationship("User", foreign_keys=[customer_id], backref="orders")
|
||||
restaurant = relationship("Restaurant", backref="orders")
|
||||
delivery = relationship("Delivery", back_populates="order")
|
||||
order_items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Order {self.id}>"
|
||||
|
||||
|
||||
class OrderItem(BaseModel, TimestampMixin):
|
||||
"""Order item model."""
|
||||
|
||||
order_id = Column(Integer, ForeignKey("order.id"), nullable=False)
|
||||
menu_item_id = Column(Integer, ForeignKey("menuitem.id"), nullable=False)
|
||||
quantity = Column(Integer, nullable=False)
|
||||
unit_price = Column(Float, nullable=False)
|
||||
special_instructions = Column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
order = relationship("Order", back_populates="order_items")
|
||||
menu_item = relationship("MenuItem", back_populates="order_items")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<OrderItem {self.id}>"
|
28
app/models/restaurant.py
Normal file
28
app/models/restaurant.py
Normal file
@ -0,0 +1,28 @@
|
||||
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin
|
||||
|
||||
|
||||
class Restaurant(BaseModel, TimestampMixin):
|
||||
"""Restaurant model."""
|
||||
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(String, nullable=True)
|
||||
address = Column(String, nullable=False)
|
||||
phone = Column(String, nullable=False)
|
||||
email = Column(String, nullable=True)
|
||||
logo_url = Column(String, nullable=True)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
latitude = Column(Float, nullable=True)
|
||||
longitude = Column(Float, nullable=True)
|
||||
opening_time = Column(String, nullable=True) # Format: HH:MM
|
||||
closing_time = Column(String, nullable=True) # Format: HH:MM
|
||||
owner_id = Column(Integer, ForeignKey("user.id"), nullable=False)
|
||||
|
||||
# Relationships
|
||||
owner = relationship("User", backref="owned_restaurants")
|
||||
menu_items = relationship("MenuItem", back_populates="restaurant", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Restaurant {self.name}>"
|
35
app/models/user.py
Normal file
35
app/models/user.py
Normal file
@ -0,0 +1,35 @@
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import Boolean, Column, Enum as SQLEnum, String
|
||||
|
||||
from app.models.base import BaseModel, TimestampMixin
|
||||
|
||||
|
||||
class UserRole(str, Enum):
|
||||
"""Enum for user roles."""
|
||||
|
||||
ADMIN = "admin"
|
||||
CUSTOMER = "customer"
|
||||
RESTAURANT_OWNER = "restaurant_owner"
|
||||
DELIVERY_DRIVER = "delivery_driver"
|
||||
|
||||
|
||||
class User(BaseModel, TimestampMixin):
|
||||
"""User model."""
|
||||
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=False)
|
||||
first_name = Column(String, nullable=False)
|
||||
last_name = Column(String, nullable=False)
|
||||
phone = Column(String, nullable=True)
|
||||
address = Column(String, nullable=True)
|
||||
role = Column(SQLEnum(UserRole), default=UserRole.CUSTOMER, nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
"""Return the user's full name."""
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<User {self.email}>"
|
Loading…
x
Reference in New Issue
Block a user