diff --git a/makefile b/makefile index 0e1fb01..7c0a7b4 100644 --- a/makefile +++ b/makefile @@ -6,6 +6,6 @@ VENV=source .venv/bin/activate; run: ${VENV} python3 main.py m_gen: - alembic revision --autogenerate + ${VENV} alembic revision --autogenerate m_up: - alembic upgrade head + ${VENV} alembic upgrade head diff --git a/src/migrations/env.py b/src/migrations/env.py index 36112a3..5a13e40 100644 --- a/src/migrations/env.py +++ b/src/migrations/env.py @@ -1,5 +1,9 @@ from logging.config import fileConfig + +from src.models.database_models.model import engine, Model +import src.models.database_models.model as models + from sqlalchemy import engine_from_config from sqlalchemy import pool @@ -18,7 +22,8 @@ if config.config_file_name is not None: # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -target_metadata = None +target_metadata = Model.metadata +config.set_main_option("sqlalchemy.url", engine.url.render_as_string(hide_password=False)) # other values from the config, defined by the needs of env.py, # can be acquired: @@ -65,7 +70,7 @@ def run_migrations_online() -> None: with connectable.connect() as connection: context.configure( - connection=connection, target_metadata=target_metadata + connection=connection, target_metadata=target_metadata, render_as_batch=True ) with context.begin_transaction(): diff --git a/src/migrations/versions/83b4c2ae88d3_.py b/src/migrations/versions/83b4c2ae88d3_.py new file mode 100644 index 0000000..bbfec5d --- /dev/null +++ b/src/migrations/versions/83b4c2ae88d3_.py @@ -0,0 +1,103 @@ +"""empty message + +Revision ID: 83b4c2ae88d3 +Revises: +Create Date: 2026-07-17 15:00:15.263996 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '83b4c2ae88d3' +down_revision: Union[str, Sequence[str], None] = None +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('groups_of_permissions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('group', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_groups_of_permissions')), + sa.UniqueConstraint('group', name=op.f('uq_groups_of_permissions_group')) + ) + with op.batch_alter_table('groups_of_permissions', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_groups_of_permissions_id'), ['id'], unique=False) + + op.create_table('permissions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('permission', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_permissions')), + sa.UniqueConstraint('permission', name=op.f('uq_permissions_permission')) + ) + with op.batch_alter_table('permissions', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_permissions_id'), ['id'], unique=False) + + op.create_table('users', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('first_name', sa.String(length=64), nullable=False), + sa.Column('last_name', sa.String(length=64), nullable=False), + sa.Column('middle_name', sa.String(length=64), nullable=False), + sa.Column('email', sa.String(length=255), nullable=False), + sa.Column('status', sa.Boolean(), nullable=False), + sa.Column('hashed_password', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_users')) + ) + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_users_email'), ['email'], unique=True) + batch_op.create_index(batch_op.f('ix_users_first_name'), ['first_name'], unique=False) + batch_op.create_index(batch_op.f('ix_users_last_name'), ['last_name'], unique=False) + batch_op.create_index(batch_op.f('ix_users_middle_name'), ['middle_name'], unique=False) + + op.create_table('group_permission', + sa.Column('group_id', sa.Integer(), nullable=False), + sa.Column('permission_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['group_id'], ['groups_of_permissions.id'], name=op.f('fk_group_permission_group_id_groups_of_permissions')), + sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], name=op.f('fk_group_permission_permission_id_permissions')), + sa.PrimaryKeyConstraint('group_id', 'permission_id', name=op.f('pk_group_permission')) + ) + op.create_table('user_direct_permissions', + sa.Column('user_id', sa.Uuid(), nullable=False), + sa.Column('permission_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], name=op.f('fk_user_direct_permissions_permission_id_permissions')), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_user_direct_permissions_user_id_users')), + sa.PrimaryKeyConstraint('user_id', 'permission_id', name=op.f('pk_user_direct_permissions')) + ) + op.create_table('user_group', + sa.Column('user_id', sa.Uuid(), nullable=False), + sa.Column('permission_group_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['permission_group_id'], ['groups_of_permissions.id'], name=op.f('fk_user_group_permission_group_id_groups_of_permissions')), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_user_group_user_id_users')), + sa.PrimaryKeyConstraint('user_id', 'permission_group_id', name=op.f('pk_user_group')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('user_group') + op.drop_table('user_direct_permissions') + op.drop_table('group_permission') + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_users_middle_name')) + batch_op.drop_index(batch_op.f('ix_users_last_name')) + batch_op.drop_index(batch_op.f('ix_users_first_name')) + batch_op.drop_index(batch_op.f('ix_users_email')) + + op.drop_table('users') + with op.batch_alter_table('permissions', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_permissions_id')) + + op.drop_table('permissions') + with op.batch_alter_table('groups_of_permissions', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_groups_of_permissions_id')) + + op.drop_table('groups_of_permissions') + # ### end Alembic commands ### diff --git a/src/models/database_models/model.py b/src/models/database_models/model.py index b32cbfd..1e0b3f5 100644 --- a/src/models/database_models/model.py +++ b/src/models/database_models/model.py @@ -21,26 +21,56 @@ class User(Model): last_name:Mapped[str]=mapped_column(String(64), index=True) middle_name:Mapped[str]=mapped_column(String(64), index=True) email:Mapped[str]=mapped_column(String(255), index=True, unique=True) - status:Mapped[bool]=mapped_column(Boolean) + status:Mapped[bool]=mapped_column(Boolean, default=True) hashed_password:Mapped[str]=mapped_column(String(255)) + + group:Mapped[list['PermissionsGroups']]=relationship(secondary="user_group", back_populates="user", lazy="selectin") - permissions:Mapped[list["Permissions"]]=relationship(secondary="user_permission", back_populates="user", lazy="selectin") + direct_permissions:Mapped[list['Permissions']]=relationship(secondary="user_direct_permissions", back_populates="users_direct") def __repr__(self) -> str: return f"ID: {self.id}, Name: {self.first_name}, Status: {self.status}" -user_permission = Table( - "user_permission", - Model.metadata, - Column("user_id", ForeignKey("users.id"), primary_key=True), - Column("permission_id", ForeignKey("permissions.id"), primary_key=True), -) - +class PermissionsGroups(Model): + __tablename__="groups_of_permissions" + + id:Mapped[int]=mapped_column(primary_key=True, index=True) + group:Mapped[str]=mapped_column(String(255), unique=True) + + user:Mapped[list['User']]=relationship(secondary="user_group", back_populates="group") + permissions:Mapped[list["Permissions"]]=relationship(secondary="group_permission", back_populates="group", lazy="selectin") + + def __repr__(self)->str: + return f"ID: {self.id}, Permissions: {self.group}" + class Permissions(Model): __tablename__ = "permissions" id:Mapped[int]=mapped_column(primary_key=True, index=True) permission:Mapped[str]=mapped_column(String(255), unique=True) - user:Mapped[list['User']]=relationship(secondary="user_permission", back_populates="permissions") + group:Mapped[list['PermissionsGroups']]=relationship(secondary="group_permission", back_populates="permissions") + users_direct:Mapped[list["User"]]=relationship(secondary="user_direct_permissions", back_populates="direct_permissions") + def __repr__(self)->str: - return f"ID: {self.id}, Permissions: {self.permission}" \ No newline at end of file + return f"ID: {self.id}, Permissions: {self.permission}" + +user_group_of_permissions=Table( + "user_group", + Model.metadata, + Column("user_id", ForeignKey("users.id"), primary_key=True), + Column("permission_group_id", ForeignKey("groups_of_permissions.id"), primary_key=True) +) + +user_permission=Table( + "user_direct_permissions", + Model.metadata, + Column("user_id",ForeignKey("users.id"), primary_key=True), + Column("permission_id", ForeignKey("permissions.id"), primary_key=True) +) + +group_permission = Table( + "group_permission", + Model.metadata, + Column("group_id", ForeignKey("groups_of_permissions.id"), primary_key=True), + Column("permission_id", ForeignKey("permissions.id"), primary_key=True), +) \ No newline at end of file