Refactor and reorganize Alembic and database configuration.

Moved Alembic files into the `app/alembic` directory and updated related paths. Consolidated database configuration in `config.py`, leveraging environment variables and ensuring centralized management. Updated Docker Compose to include `.env` files, providing a more consistent environment setup.
This commit is contained in:
2025-02-28 09:26:25 +01:00
parent b02d38f5b2
commit 481b6d618e
13 changed files with 74 additions and 41 deletions

View File

@@ -0,0 +1 @@
Generic single-database configuration.

View File

View File

@@ -0,0 +1,88 @@
import sys
from logging.config import fileConfig
from pathlib import Path
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# Get the path to the app directory (parent of 'alembic')
app_dir = Path(__file__).resolve().parent.parent
# Add the app directory to Python path
sys.path.append(str(app_dir.parent))
# Import Core modules
from app.core.config import settings
from app.core.database import Base
# Import all models to ensure they're registered with SQLAlchemy
from app.models import *
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
target_metadata = Base.metadata
# Override the SQLAlchemy URL with the one from settings
config.set_main_option("sqlalchemy.url", settings.database_url)
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@@ -0,0 +1,26 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1,26 @@
"""Initial empty migration
Revision ID: 7396957cbe80
Revises:
Create Date: 2025-02-27 12:47:46.445313
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '7396957cbe80'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
pass
def downgrade() -> None:
pass

View File

View File

@@ -1,29 +0,0 @@
from pydantic_settings import BaseSettings
from typing import Optional, List
class Settings(BaseSettings):
PROJECT_NAME: Optional[str] = "App"
VERSION: Optional[str] = "1.0.0"
API_V1_STR: Optional[str] = "/api/v1"
# Database configuration
DATABASE_URL: Optional[str] = None
# JWT configuration
SECRET_KEY: Optional[str] = None
ALGORITHM: Optional[str] = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: Optional[int] = 30
# CORS configuration
BACKEND_CORS_ORIGINS: Optional[List[str]] = ["http://localhost:3000"] # Frontend URL
# Admin user
FIRST_SUPERUSER_EMAIL: Optional[str] = None
FIRST_SUPERUSER_PASSWORD: Optional[str] = None
class Config:
env_file = ".env"
settings = Settings()

View File

@@ -0,0 +1,48 @@
from pydantic_settings import BaseSettings
from typing import Optional, List
class Settings(BaseSettings):
PROJECT_NAME: str = "App"
VERSION: str = "1.0.0"
API_V1_STR: str = "/api/v1"
# Database configuration
POSTGRES_USER: str = "postgres"
POSTGRES_PASSWORD: str = "postgres"
POSTGRES_HOST: str = "localhost"
POSTGRES_PORT: str = "5432"
POSTGRES_DB: str = "app"
DATABASE_URL: Optional[str] = None
@property
def database_url(self) -> str:
"""
Get the SQLAlchemy database URL.
If DATABASE_URL is explicitly set, use that.
Otherwise, construct from components.
"""
if self.DATABASE_URL:
return self.DATABASE_URL
self.DATABASE_URL = f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
return self.DATABASE_URL
# JWT configuration
SECRET_KEY: str = "your_secret_key_here"
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
# CORS configuration
BACKEND_CORS_ORIGINS: List[str] = ["http://localhost:3000"]
# Admin user
FIRST_SUPERUSER_EMAIL: Optional[str] = None
FIRST_SUPERUSER_PASSWORD: Optional[str] = None
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
case_sensitive = True
settings = Settings()

View File

@@ -1,11 +1,11 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os
SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@db:5432/app")
from app.core.config import settings
engine = create_engine(SQLALCHEMY_DATABASE_URL)
# Use the database URL from settings
engine = create_engine(settings.database_url)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()