Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ COPY pyproject.toml pyproject.toml

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev
RUN poetry install --no-root

COPY . /app

Expand Down
233 changes: 233 additions & 0 deletions migrations/versions/5cb1f4f47531_add_additionalproducts_and_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
"""Add AdditionalProducts and RecommendedProducts models

Revision ID: 5cb1f4f47531
Revises: 799909f2b79d
Create Date: 2025-09-08 14:47:17.898500

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
import sqlmodel
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = '5cb1f4f47531'
down_revision: Union[str, None] = '799909f2b79d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('company',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('hashed_password', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('is_admin', sa.Boolean(), nullable=False),
sa.Column('is_staff', sa.Boolean(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('first_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('last_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.alter_column('categories', 'title',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('categories', 'description',
existing_type=sa.TEXT(),
type_=sqlmodel.sql.sqltypes.AutoString(),
existing_nullable=True)
op.alter_column('categories', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=False)
op.drop_index(op.f('ix_categories_id'), table_name='categories')
op.alter_column('product_categories', 'product_id',
existing_type=sa.INTEGER(),
nullable=False)
op.alter_column('product_categories', 'category_id',
existing_type=sa.INTEGER(),
nullable=False)
op.drop_index(op.f('ix_product_categories_id'), table_name='product_categories')
op.alter_column('product_discounts', 'product_id',
existing_type=sa.INTEGER(),
nullable=False)
op.alter_column('product_discounts', 'discount_amount',
existing_type=sa.NUMERIC(),
type_=sa.Float(),
existing_nullable=True)
op.alter_column('product_discounts', 'valid_from',
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.alter_column('product_discounts', 'valid_to',
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.drop_index(op.f('ix_product_discounts_id'), table_name='product_discounts')
op.alter_column('product_images', 'product_id',
existing_type=sa.INTEGER(),
nullable=False)
op.drop_index(op.f('ix_product_images_id'), table_name='product_images')
op.alter_column('products', 'title',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('products', 'description',
existing_type=sa.TEXT(),
type_=sqlmodel.sql.sqltypes.AutoString(),
existing_nullable=True)
op.alter_column('products', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=False)
op.drop_index(op.f('ix_products_id'), table_name='products')
op.alter_column('stock_records', 'product_id',
existing_type=sa.INTEGER(),
nullable=False)
op.alter_column('stock_records', 'price',
existing_type=sa.NUMERIC(),
type_=sa.Float(),
nullable=False)
op.alter_column('stock_records', 'quantity',
existing_type=sa.INTEGER(),
nullable=False)
op.alter_column('stock_records', 'date_created',
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.drop_index(op.f('ix_stock_records_id'), table_name='stock_records')
op.alter_column('user_addresses', 'city',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('user_addresses', 'street',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('user_addresses', 'house',
existing_type=sa.VARCHAR(),
nullable=False)
op.drop_index(op.f('ix_user_addresses_id'), table_name='user_addresses')
op.alter_column('users', 'email',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('users', 'phone_number',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('users', 'is_admin',
existing_type=sa.BOOLEAN(),
nullable=False)
op.alter_column('users', 'is_staff',
existing_type=sa.BOOLEAN(),
nullable=False)
op.alter_column('users', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=False)
op.alter_column('users', 'date_joined',
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.alter_column('users', 'is_temporary',
existing_type=sa.BOOLEAN(),
nullable=False)
op.drop_index(op.f('ix_users_email'), table_name='users')
op.drop_index(op.f('ix_users_id'), table_name='users')
op.drop_index(op.f('ix_users_phone_number'), table_name='users')
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_index(op.f('ix_users_phone_number'), 'users', ['phone_number'], unique=True)
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.alter_column('users', 'is_temporary',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('users', 'date_joined',
existing_type=postgresql.TIMESTAMP(),
nullable=True)
op.alter_column('users', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('users', 'is_staff',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('users', 'is_admin',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('users', 'phone_number',
existing_type=sa.VARCHAR(),
nullable=True)
op.alter_column('users', 'email',
existing_type=sa.VARCHAR(),
nullable=True)
op.create_index(op.f('ix_user_addresses_id'), 'user_addresses', ['id'], unique=False)
op.alter_column('user_addresses', 'house',
existing_type=sa.VARCHAR(),
nullable=True)
op.alter_column('user_addresses', 'street',
existing_type=sa.VARCHAR(),
nullable=True)
op.alter_column('user_addresses', 'city',
existing_type=sa.VARCHAR(),
nullable=True)
op.create_index(op.f('ix_stock_records_id'), 'stock_records', ['id'], unique=False)
op.alter_column('stock_records', 'date_created',
existing_type=postgresql.TIMESTAMP(),
nullable=True)
op.alter_column('stock_records', 'quantity',
existing_type=sa.INTEGER(),
nullable=True)
op.alter_column('stock_records', 'price',
existing_type=sa.Float(),
type_=sa.NUMERIC(),
nullable=True)
op.alter_column('stock_records', 'product_id',
existing_type=sa.INTEGER(),
nullable=True)
op.create_index(op.f('ix_products_id'), 'products', ['id'], unique=False)
op.alter_column('products', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('products', 'description',
existing_type=sqlmodel.sql.sqltypes.AutoString(),
type_=sa.TEXT(),
existing_nullable=True)
op.alter_column('products', 'title',
existing_type=sa.VARCHAR(),
nullable=True)
op.create_index(op.f('ix_product_images_id'), 'product_images', ['id'], unique=False)
op.alter_column('product_images', 'product_id',
existing_type=sa.INTEGER(),
nullable=True)
op.create_index(op.f('ix_product_discounts_id'), 'product_discounts', ['id'], unique=False)
op.alter_column('product_discounts', 'valid_to',
existing_type=postgresql.TIMESTAMP(),
nullable=True)
op.alter_column('product_discounts', 'valid_from',
existing_type=postgresql.TIMESTAMP(),
nullable=True)
op.alter_column('product_discounts', 'discount_amount',
existing_type=sa.Float(),
type_=sa.NUMERIC(),
existing_nullable=True)
op.alter_column('product_discounts', 'product_id',
existing_type=sa.INTEGER(),
nullable=True)
op.create_index(op.f('ix_product_categories_id'), 'product_categories', ['id'], unique=False)
op.alter_column('product_categories', 'category_id',
existing_type=sa.INTEGER(),
nullable=True)
op.alter_column('product_categories', 'product_id',
existing_type=sa.INTEGER(),
nullable=True)
op.create_index(op.f('ix_categories_id'), 'categories', ['id'], unique=False)
op.alter_column('categories', 'is_active',
existing_type=sa.BOOLEAN(),
nullable=True)
op.alter_column('categories', 'description',
existing_type=sqlmodel.sql.sqltypes.AutoString(),
type_=sa.TEXT(),
existing_nullable=True)
op.alter_column('categories', 'title',
existing_type=sa.VARCHAR(),
nullable=True)
op.drop_table('company')
# ### end Alembic commands ###
58 changes: 58 additions & 0 deletions src/catalogue/models/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ class Product(SQLModel, table=True):
images: List["ProductImage"] = Relationship(back_populates="product")
stock_records: List["StockRecord"] = Relationship(back_populates="product")
discounts: List["ProductDiscount"] = Relationship(back_populates="product")

# Relationships for Additional Products
additional_products_as_primary: List["AdditionalProducts"] = Relationship(
back_populates="primary_product",
sa_relationship_kwargs={"foreign_keys": "AdditionalProducts.primary_id"}
)
additional_products_as_additional: List["AdditionalProducts"] = Relationship(
back_populates="additional_product",
sa_relationship_kwargs={"foreign_keys": "AdditionalProducts.additional_id"}
)

# Relationships for Recommended Products
recommended_products_as_primary: List["RecommendedProducts"] = Relationship(
back_populates="primary_product",
sa_relationship_kwargs={"foreign_keys": "RecommendedProducts.primary_id"}
)
recommended_products_as_recommended: List["RecommendedProducts"] = Relationship(
back_populates="recommended_product",
sa_relationship_kwargs={"foreign_keys": "RecommendedProducts.recommended_id"}
)

class ProductCategory(SQLModel, table=True):
__tablename__ = 'product_categories'
Expand Down Expand Up @@ -90,3 +110,41 @@ class ProductDiscount(SQLModel, table=True):
valid_to: datetime

product: Product = Relationship(back_populates="discounts")


class AdditionalProducts(SQLModel, table=True):
"""Model for additional products."""
__tablename__ = 'additional_products'

id: Optional[int] = Field(default=None, primary_key=True)
primary_id: int = Field(foreign_key="products.id")
additional_id: int = Field(foreign_key="products.id")

# Relationships
primary_product: Product = Relationship(
back_populates="additional_products_as_primary",
sa_relationship_kwargs={"foreign_keys": "AdditionalProducts.primary_id"}
)
additional_product: Product = Relationship(
back_populates="additional_products_as_additional",
sa_relationship_kwargs={"foreign_keys": "AdditionalProducts.additional_id"}
)


class RecommendedProducts(SQLModel, table=True):
"""Model for recommended products."""
__tablename__ = 'recommended_products'

id: Optional[int] = Field(default=None, primary_key=True)
primary_id: int = Field(foreign_key="products.id")
recommended_id: int = Field(foreign_key="products.id")

# Relationships
primary_product: Product = Relationship(
back_populates="recommended_products_as_primary",
sa_relationship_kwargs={"foreign_keys": "RecommendedProducts.primary_id"}
)
recommended_product: Product = Relationship(
back_populates="recommended_products_as_recommended",
sa_relationship_kwargs={"foreign_keys": "RecommendedProducts.recommended_id"}
)
28 changes: 27 additions & 1 deletion src/catalogue/repository.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession

from src.catalogue.models.database import Product
from src.catalogue.models.database import (
Product,
AdditionalProducts,
RecommendedProducts,
)
from src.common.databases.postgres import get_session
from src.common.repository.sqlalchemy import BaseSQLAlchemyRepository

Expand All @@ -11,5 +15,27 @@ def __init__(self, session: AsyncSession):
super().__init__(model=Product, session=session)


class AdditionalProductsRepository(BaseSQLAlchemyRepository[AdditionalProducts]):
def __init__(self, session: AsyncSession):
super().__init__(model=AdditionalProducts, session=session)


class RecommendedProductsRepository(BaseSQLAlchemyRepository[RecommendedProducts]):
def __init__(self, session: AsyncSession):
super().__init__(model=RecommendedProducts, session=session)


def get_product_repository(session: AsyncSession = Depends(get_session)) -> ProductRepository:
return ProductRepository(session=session)


def get_additional_products_repository(
session: AsyncSession = Depends(get_session)
) -> AdditionalProductsRepository:
return AdditionalProductsRepository(session=session)


def get_recommended_products_repository(
session: AsyncSession = Depends(get_session)
) -> RecommendedProductsRepository:
return RecommendedProductsRepository(session=session)
Loading