From 95996d22f8aa76a8fda859b1ba944a5bd596229a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 14:33:07 +0200 Subject: [PATCH 1/6] feat(models): add basic database structure --- .../versions/a60134ef6c4c_add_categories.py | 48 +++++++++++++++++++ ...9d6c59812_user_transaction_relationship.py | 41 ++++++++++++++++ 7project/backend/app/core/db.py | 1 + 7project/backend/app/models/categories.py | 22 +++++++++ 7project/backend/app/models/transaction.py | 8 +++- 7project/backend/app/models/user.py | 6 +++ 7project/create_migration.sh | 4 ++ 7 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 7project/backend/alembic/versions/a60134ef6c4c_add_categories.py create mode 100644 7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py create mode 100644 7project/backend/app/models/categories.py diff --git a/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py b/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py new file mode 100644 index 0000000..7c1e599 --- /dev/null +++ b/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py @@ -0,0 +1,48 @@ +"""add categories + +Revision ID: a60134ef6c4c +Revises: c5e9d6c59812 +Create Date: 2025-10-09 14:29:31.170347 + +""" +from typing import Sequence, Union + +import fastapi_users_db_sqlalchemy +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'a60134ef6c4c' +down_revision: Union[str, Sequence[str], None] = 'c5e9d6c59812' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('categories', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('category_transaction', + sa.Column('id_category', sa.Integer(), nullable=True), + sa.Column('id_transaction', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['id_category'], ['categories.id'], ), + sa.ForeignKeyConstraint(['id_transaction'], ['transaction.id'], ) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('category_transaction') + op.drop_table('categories') + # ### end Alembic commands ### diff --git a/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py b/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py new file mode 100644 index 0000000..b88a489 --- /dev/null +++ b/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py @@ -0,0 +1,41 @@ +"""User transaction relationship + +Revision ID: c5e9d6c59812 +Revises: 81f275275556 +Create Date: 2025-10-09 14:17:19.983889 + +""" +from typing import Sequence, Union + +import fastapi_users_db_sqlalchemy +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c5e9d6c59812' +down_revision: Union[str, Sequence[str], None] = '81f275275556' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('transaction', 'user_id', + existing_type=sa.UUID(), + type_=fastapi_users_db_sqlalchemy.generics.GUID(), + existing_nullable=False) + op.create_foreign_key(None, 'transaction', 'user', ['user_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'transaction', type_='foreignkey') + op.alter_column('transaction', 'user_id', + existing_type=fastapi_users_db_sqlalchemy.generics.GUID(), + type_=sa.UUID(), + existing_nullable=False) + # ### end Alembic commands ### diff --git a/7project/backend/app/core/db.py b/7project/backend/app/core/db.py index 40ff77f..9c8cad7 100644 --- a/7project/backend/app/core/db.py +++ b/7project/backend/app/core/db.py @@ -17,6 +17,7 @@ if not DATABASE_URL: # Load all models to register them from app.models.user import User from app.models.transaction import Transaction +from app.models.categories import Category ssl_enabled = os.getenv("MARIADB_HOST", "localhost") != "localhost" connect_args = {"ssl": {"ssl": True}} if ssl_enabled else {} diff --git a/7project/backend/app/models/categories.py b/7project/backend/app/models/categories.py new file mode 100644 index 0000000..641ca2d --- /dev/null +++ b/7project/backend/app/models/categories.py @@ -0,0 +1,22 @@ +from fastapi_users_db_sqlalchemy import GUID +from sqlalchemy import Column, Integer, String, ForeignKey, Table +from sqlalchemy.orm import relationship + +from app.core.base import Base + +association_table = Table( + "category_transaction", + Base.metadata, + Column("id_category", Integer, ForeignKey("categories.id")), + Column("id_transaction", Integer, ForeignKey("transaction.id")) +) + + +class Category(Base): + __tablename__ = "categories" + id = Column(Integer, primary_key=True, autoincrement=True) + name = Column(String(length=100), unique=True, nullable=False) + description = Column(String(length=255), nullable=True) + user_id = Column(GUID, ForeignKey("user.id"), nullable=False) + user = relationship("User", back_populates="categories") + relationship(secondary=association_table) diff --git a/7project/backend/app/models/transaction.py b/7project/backend/app/models/transaction.py index bc13565..39b640d 100644 --- a/7project/backend/app/models/transaction.py +++ b/7project/backend/app/models/transaction.py @@ -1,9 +1,15 @@ -from sqlalchemy import Column, Integer, String, Float +from fastapi_users_db_sqlalchemy import GUID +from sqlalchemy import Column, Integer, String, Float, ForeignKey, UUID +from sqlalchemy.orm import relationship from app.core.base import Base + class Transaction(Base): __tablename__ = "transaction" id = Column(Integer, primary_key=True, autoincrement=True) amount = Column(Float, nullable=False) description = Column(String(length=255), nullable=True) + user_id = Column(GUID, ForeignKey("user.id"), nullable=False) + # Relationship + user = relationship("User", back_populates="transactions") diff --git a/7project/backend/app/models/user.py b/7project/backend/app/models/user.py index 4c0f206..836dddf 100644 --- a/7project/backend/app/models/user.py +++ b/7project/backend/app/models/user.py @@ -1,7 +1,13 @@ from sqlalchemy import Column, String +from sqlalchemy.orm import relationship from fastapi_users.db import SQLAlchemyBaseUserTableUUID from app.core.base import Base + class User(SQLAlchemyBaseUserTableUUID, Base): first_name = Column(String(length=100), nullable=True) last_name = Column(String(length=100), nullable=True) + + # Relationship + transactions = relationship("Transaction", back_populates="user") + categories = relationship("Category", back_populates="user") \ No newline at end of file diff --git a/7project/create_migration.sh b/7project/create_migration.sh index de43ffc..f8e8c04 100644 --- a/7project/create_migration.sh +++ b/7project/create_migration.sh @@ -8,4 +8,8 @@ fi cd backend || { echo "Directory 'backend' does not exist"; exit 1; } alembic revision --autogenerate -m "$1" git add alembic/versions/* +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${YELLOW}Don't forget to check imports in the new migration file!${NC}" cd - || exit \ No newline at end of file From e32e18f0de92dcbb0a31552112a778d34bdc11f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 14:41:11 +0200 Subject: [PATCH 2/6] feat(models): add basic database structure --- 7project/charts/myapp-chart/templates/app-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/7project/charts/myapp-chart/templates/app-deployment.yaml b/7project/charts/myapp-chart/templates/app-deployment.yaml index 9011d46..1d264ab 100644 --- a/7project/charts/myapp-chart/templates/app-deployment.yaml +++ b/7project/charts/myapp-chart/templates/app-deployment.yaml @@ -25,7 +25,7 @@ spec: - containerPort: {{ .Values.app.port }} env: - name: MARIADB_HOST - value: {{ printf "%s.%s.svc.cluster.local" .Values.mariadb.mariaDbRef.name .Values.mariadb.mariaDbRef.namespace | quote }} + value: "mariadb-repl-maxscale-internal.mariadb-operator.svc.cluster.local" - name: MARIADB_PORT value: '3306' - name: MARIADB_DB From 8db669ac723914ed9e49bf2ef991413e72f649b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 14:56:51 +0200 Subject: [PATCH 3/6] feat(infrastructure): database changes --- 7project/backend/alembic.ini | 2 +- ...10_09_1456-63e072f09836_add_categories.py} | 41 +++++++++++----- .../versions/a60134ef6c4c_add_categories.py | 48 ------------------- ...9d6c59812_user_transaction_relationship.py | 41 ---------------- .../maxscale/charts/maxscale-helm/Chart.yaml | 2 +- .../templates/phpmyadmin-deployment.yaml | 2 +- 7project/tofu/modules/maxscale/main.tf | 2 +- 7 files changed, 34 insertions(+), 104 deletions(-) rename 7project/backend/alembic/versions/{81f275275556_init_migration.py => 2025_10_09_1456-63e072f09836_add_categories.py} (60%) delete mode 100644 7project/backend/alembic/versions/a60134ef6c4c_add_categories.py delete mode 100644 7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py diff --git a/7project/backend/alembic.ini b/7project/backend/alembic.ini index c6931a7..6dca9ab 100644 --- a/7project/backend/alembic.ini +++ b/7project/backend/alembic.ini @@ -11,7 +11,7 @@ script_location = %(here)s/alembic # Uncomment the line below if you want the files to be prepended with date and time # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file # for all available tokens -# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s +file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s # sys.path path, will be prepended to sys.path if present. # defaults to the current working directory. for multiple paths, the path separator diff --git a/7project/backend/alembic/versions/81f275275556_init_migration.py b/7project/backend/alembic/versions/2025_10_09_1456-63e072f09836_add_categories.py similarity index 60% rename from 7project/backend/alembic/versions/81f275275556_init_migration.py rename to 7project/backend/alembic/versions/2025_10_09_1456-63e072f09836_add_categories.py index 097cc09..1ddf813 100644 --- a/7project/backend/alembic/versions/81f275275556_init_migration.py +++ b/7project/backend/alembic/versions/2025_10_09_1456-63e072f09836_add_categories.py @@ -1,8 +1,8 @@ -"""Init migration +"""add categories -Revision ID: 81f275275556 +Revision ID: 63e072f09836 Revises: -Create Date: 2025-09-24 17:39:25.346690 +Create Date: 2025-10-09 14:56:14.653249 """ from typing import Sequence, Union @@ -13,7 +13,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision: str = '81f275275556' +revision: str = '63e072f09836' down_revision: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -22,12 +22,6 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.create_table('transaction', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('amount', sa.Float(), nullable=False), - sa.Column('description', sa.String(length=255), nullable=True), - sa.PrimaryKeyConstraint('id') - ) op.create_table('user', sa.Column('first_name', sa.String(length=100), nullable=True), sa.Column('last_name', sa.String(length=100), nullable=True), @@ -40,13 +34,38 @@ def upgrade() -> None: sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_table('categories', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('transaction', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('amount', sa.Float(), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('category_transaction', + sa.Column('id_category', sa.Integer(), nullable=True), + sa.Column('id_transaction', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['id_category'], ['categories.id'], ), + sa.ForeignKeyConstraint(['id_transaction'], ['transaction.id'], ) + ) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('category_transaction') + op.drop_table('transaction') + op.drop_table('categories') op.drop_index(op.f('ix_user_email'), table_name='user') op.drop_table('user') - op.drop_table('transaction') # ### end Alembic commands ### diff --git a/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py b/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py deleted file mode 100644 index 7c1e599..0000000 --- a/7project/backend/alembic/versions/a60134ef6c4c_add_categories.py +++ /dev/null @@ -1,48 +0,0 @@ -"""add categories - -Revision ID: a60134ef6c4c -Revises: c5e9d6c59812 -Create Date: 2025-10-09 14:29:31.170347 - -""" -from typing import Sequence, Union - -import fastapi_users_db_sqlalchemy -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'a60134ef6c4c' -down_revision: Union[str, Sequence[str], None] = 'c5e9d6c59812' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('categories', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(length=100), nullable=False), - sa.Column('description', sa.String(length=255), nullable=True), - sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), - sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('name') - ) - op.create_table('category_transaction', - sa.Column('id_category', sa.Integer(), nullable=True), - sa.Column('id_transaction', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['id_category'], ['categories.id'], ), - sa.ForeignKeyConstraint(['id_transaction'], ['transaction.id'], ) - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('category_transaction') - op.drop_table('categories') - # ### end Alembic commands ### diff --git a/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py b/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py deleted file mode 100644 index b88a489..0000000 --- a/7project/backend/alembic/versions/c5e9d6c59812_user_transaction_relationship.py +++ /dev/null @@ -1,41 +0,0 @@ -"""User transaction relationship - -Revision ID: c5e9d6c59812 -Revises: 81f275275556 -Create Date: 2025-10-09 14:17:19.983889 - -""" -from typing import Sequence, Union - -import fastapi_users_db_sqlalchemy -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'c5e9d6c59812' -down_revision: Union[str, Sequence[str], None] = '81f275275556' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('transaction', 'user_id', - existing_type=sa.UUID(), - type_=fastapi_users_db_sqlalchemy.generics.GUID(), - existing_nullable=False) - op.create_foreign_key(None, 'transaction', 'user', ['user_id'], ['id']) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'transaction', type_='foreignkey') - op.alter_column('transaction', 'user_id', - existing_type=fastapi_users_db_sqlalchemy.generics.GUID(), - type_=sa.UUID(), - existing_nullable=False) - # ### end Alembic commands ### diff --git a/7project/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml b/7project/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml index 15afe88..c78c909 100644 --- a/7project/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml +++ b/7project/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v2 name: maxscale-helm -version: 1.0.7 +version: 1.0.8 description: Helm chart for MaxScale related Kubernetes manifests diff --git a/7project/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-deployment.yaml b/7project/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-deployment.yaml index 4a0156c..1ee7b38 100644 --- a/7project/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-deployment.yaml +++ b/7project/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-deployment.yaml @@ -28,7 +28,7 @@ spec: - name: DATABASE_ENABLE_SSL value: "yes" - name: DATABASE_HOST - value: "mariadb-repl" + value: "mariadb-repl-maxscale-internal" - name: DATABASE_PORT_NUMBER value: "3306" - name: PHPMYADMIN_ALLOW_NO_PASSWORD diff --git a/7project/tofu/modules/maxscale/main.tf b/7project/tofu/modules/maxscale/main.tf index 2b44909..af5a401 100644 --- a/7project/tofu/modules/maxscale/main.tf +++ b/7project/tofu/modules/maxscale/main.tf @@ -58,7 +58,7 @@ resource "helm_release" "mariadb-operator" { resource "helm_release" "maxscale_helm" { name = "maxscale-helm" chart = "${path.module}/charts/maxscale-helm" - version = "1.0.7" + version = "1.0.8" depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ] timeout = 3600 From 72c241f4f7f407a839a4bae07ea37b594fed7355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 15:07:33 +0200 Subject: [PATCH 4/6] feat(infrastructure): database changes --- 7project/backend/app/models/categories.py | 2 +- 7project/backend/app/models/transaction.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/7project/backend/app/models/categories.py b/7project/backend/app/models/categories.py index 641ca2d..af7ef01 100644 --- a/7project/backend/app/models/categories.py +++ b/7project/backend/app/models/categories.py @@ -19,4 +19,4 @@ class Category(Base): description = Column(String(length=255), nullable=True) user_id = Column(GUID, ForeignKey("user.id"), nullable=False) user = relationship("User", back_populates="categories") - relationship(secondary=association_table) + relationship("Transaction", secondary=association_table, back_populates="categories") diff --git a/7project/backend/app/models/transaction.py b/7project/backend/app/models/transaction.py index 39b640d..6a7e0bf 100644 --- a/7project/backend/app/models/transaction.py +++ b/7project/backend/app/models/transaction.py @@ -1,7 +1,9 @@ +from exceptiongroup import catch from fastapi_users_db_sqlalchemy import GUID -from sqlalchemy import Column, Integer, String, Float, ForeignKey, UUID +from sqlalchemy import Column, Integer, String, Float, ForeignKey from sqlalchemy.orm import relationship from app.core.base import Base +from app.models.categories import association_table class Transaction(Base): @@ -13,3 +15,4 @@ class Transaction(Base): # Relationship user = relationship("User", back_populates="transactions") + categories = relationship("Category", secondary=association_table, back_populates="transactions") From 6040f4339cebb6deb8eef74af2acdfffe7fb8d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 15:09:26 +0200 Subject: [PATCH 5/6] feat(models): database changes --- 7project/backend/app/models/categories.py | 2 +- 7project/backend/app/models/transaction.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/7project/backend/app/models/categories.py b/7project/backend/app/models/categories.py index af7ef01..8ae0e5c 100644 --- a/7project/backend/app/models/categories.py +++ b/7project/backend/app/models/categories.py @@ -19,4 +19,4 @@ class Category(Base): description = Column(String(length=255), nullable=True) user_id = Column(GUID, ForeignKey("user.id"), nullable=False) user = relationship("User", back_populates="categories") - relationship("Transaction", secondary=association_table, back_populates="categories") + transactions = relationship("Transaction", secondary=association_table, back_populates="categories") diff --git a/7project/backend/app/models/transaction.py b/7project/backend/app/models/transaction.py index 6a7e0bf..f260400 100644 --- a/7project/backend/app/models/transaction.py +++ b/7project/backend/app/models/transaction.py @@ -1,4 +1,3 @@ -from exceptiongroup import catch from fastapi_users_db_sqlalchemy import GUID from sqlalchemy import Column, Integer, String, Float, ForeignKey from sqlalchemy.orm import relationship From abebdb019b35c30ea3ec67dbb132c7ca3ad70456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Thu, 9 Oct 2025 15:15:24 +0200 Subject: [PATCH 6/6] feat(models): change unique index --- ...4-390041bd839e_update_categories_unique.py | 34 +++++++++++++++++++ 7project/backend/app/models/categories.py | 7 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 7project/backend/alembic/versions/2025_10_09_1514-390041bd839e_update_categories_unique.py diff --git a/7project/backend/alembic/versions/2025_10_09_1514-390041bd839e_update_categories_unique.py b/7project/backend/alembic/versions/2025_10_09_1514-390041bd839e_update_categories_unique.py new file mode 100644 index 0000000..ffcc861 --- /dev/null +++ b/7project/backend/alembic/versions/2025_10_09_1514-390041bd839e_update_categories_unique.py @@ -0,0 +1,34 @@ +"""update categories unique + +Revision ID: 390041bd839e +Revises: 63e072f09836 +Create Date: 2025-10-09 15:14:31.557686 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '390041bd839e' +down_revision: Union[str, Sequence[str], None] = '63e072f09836' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('name'), table_name='categories') + op.create_unique_constraint('uix_name_user_id', 'categories', ['name', 'user_id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('uix_name_user_id', 'categories', type_='unique') + op.create_index(op.f('name'), 'categories', ['name'], unique=True) + # ### end Alembic commands ### diff --git a/7project/backend/app/models/categories.py b/7project/backend/app/models/categories.py index 8ae0e5c..bd766a3 100644 --- a/7project/backend/app/models/categories.py +++ b/7project/backend/app/models/categories.py @@ -1,5 +1,5 @@ from fastapi_users_db_sqlalchemy import GUID -from sqlalchemy import Column, Integer, String, ForeignKey, Table +from sqlalchemy import Column, Integer, String, ForeignKey, Table, UniqueConstraint from sqlalchemy.orm import relationship from app.core.base import Base @@ -14,8 +14,11 @@ association_table = Table( class Category(Base): __tablename__ = "categories" + __table_args__ = ( + UniqueConstraint("name", "user_id", name="uix_name_user_id"), + ) id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String(length=100), unique=True, nullable=False) + name = Column(String(length=100), nullable=False) description = Column(String(length=255), nullable=True) user_id = Column(GUID, ForeignKey("user.id"), nullable=False) user = relationship("User", back_populates="categories")