Create queries table
This commit is contained in:
parent
64ccb485b5
commit
985629446a
@ -18,7 +18,8 @@ class DatabaseManager:
|
|||||||
def __init__(self, database_url: str, **kwargs: dict) -> None:
|
def __init__(self, database_url: str, **kwargs: dict) -> None:
|
||||||
if DatabaseManager.__instance is None:
|
if DatabaseManager.__instance is None:
|
||||||
self.db = Alchemical(database_url, **kwargs)
|
self.db = Alchemical(database_url, **kwargs)
|
||||||
self.db.create_all()
|
# Database managed by Alembic so no create_all() required
|
||||||
|
# self.db.create_all()
|
||||||
DatabaseManager.__instance = self
|
DatabaseManager.__instance = self
|
||||||
else:
|
else:
|
||||||
raise Exception("Attempted to create a second DatabaseManager instance")
|
raise Exception("Attempted to create a second DatabaseManager instance")
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
# Standard library imports
|
# Standard library imports
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from dataclasses import asdict
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import json
|
||||||
|
|
||||||
# PyQt imports
|
# PyQt imports
|
||||||
|
|
||||||
@ -13,13 +15,37 @@ from sqlalchemy import (
|
|||||||
String,
|
String,
|
||||||
)
|
)
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
|
from sqlalchemy.engine.interfaces import Dialect
|
||||||
from sqlalchemy.orm import (
|
from sqlalchemy.orm import (
|
||||||
Mapped,
|
Mapped,
|
||||||
mapped_column,
|
mapped_column,
|
||||||
relationship,
|
relationship,
|
||||||
)
|
)
|
||||||
|
from sqlalchemy.types import TypeDecorator, TEXT
|
||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
|
from classes import Filter
|
||||||
|
|
||||||
|
|
||||||
|
class JSONEncodedDict(TypeDecorator):
|
||||||
|
"""
|
||||||
|
Custom JSON Type for MariaDB (since native JSON type is just LONGTEXT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
impl = TEXT
|
||||||
|
|
||||||
|
def process_bind_param(self, value: dict | None, dialect: Dialect) -> str | None:
|
||||||
|
"""Convert Python dictionary to JSON string before saving."""
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return json.dumps(value, default=lambda o: o.__dict__)
|
||||||
|
|
||||||
|
def process_result_value(self, value: str | None, dialect: Dialect) -> dict | None:
|
||||||
|
"""Convert JSON string back to Python dictionary after retrieval."""
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return json.loads(value)
|
||||||
|
|
||||||
|
|
||||||
# Database classes
|
# Database classes
|
||||||
@ -128,15 +154,24 @@ class QueriesTable(Model):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||||
sql: Mapped[str] = mapped_column(
|
_filter_data: Mapped[dict | None] = mapped_column("filter_data", JSONEncodedDict, nullable=True)
|
||||||
String(2048), index=False, default="", nullable=False
|
favourite: Mapped[bool] = mapped_column(Boolean, nullable=False, index=False, default=False)
|
||||||
)
|
|
||||||
favourite: Mapped[bool] = mapped_column(
|
def _get_filter(self) -> Filter:
|
||||||
Boolean, nullable=False, index=False, default=False
|
"""Convert stored JSON dictionary to a Filter object."""
|
||||||
)
|
if isinstance(self._filter_data, dict):
|
||||||
|
return Filter(**self._filter_data)
|
||||||
|
return Filter() # Default object if None or invalid data
|
||||||
|
|
||||||
|
def _set_filter(self, value: Filter | None) -> None:
|
||||||
|
"""Convert a Filter object to JSON before storing."""
|
||||||
|
self._filter_data = asdict(value) if isinstance(value, Filter) else None
|
||||||
|
|
||||||
|
# Single definition of `filter`
|
||||||
|
filter = property(_get_filter, _set_filter)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<Queries(id={self.id}, name={self.name}, sql={self.sql}>"
|
return f"<QueriesTable(id={self.id}, name={self.name}, filter={self.filter})>"
|
||||||
|
|
||||||
|
|
||||||
class SettingsTable(Model):
|
class SettingsTable(Model):
|
||||||
|
|||||||
@ -608,6 +608,25 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
|
|||||||
session.connection().execute(stmt, sqla_map)
|
session.connection().execute(stmt, sqla_map)
|
||||||
|
|
||||||
|
|
||||||
|
class Queries(dbtables.QueriesTable):
|
||||||
|
def __init__(
|
||||||
|
self, session: Session, name: str, filter: dbtables.Filter, favourite: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""Create new query"""
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.filter = filter
|
||||||
|
self.favourite = favourite
|
||||||
|
session.add(self)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_queries(cls, session: Session) -> Sequence["Queries"]:
|
||||||
|
"""Returns a list of all queries ordered by name"""
|
||||||
|
|
||||||
|
return session.scalars(select(cls).order_by(cls.name)).all()
|
||||||
|
|
||||||
|
|
||||||
class Settings(dbtables.SettingsTable):
|
class Settings(dbtables.SettingsTable):
|
||||||
def __init__(self, session: Session, name: str) -> None:
|
def __init__(self, session: Session, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|||||||
47
migrations/versions/4fc2a9a82ab0_create_queries_table.py
Normal file
47
migrations/versions/4fc2a9a82ab0_create_queries_table.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""create queries table
|
||||||
|
|
||||||
|
Revision ID: 4fc2a9a82ab0
|
||||||
|
Revises: ab475332d873
|
||||||
|
Create Date: 2025-02-26 13:13:25.118489
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import dbtables
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '4fc2a9a82ab0'
|
||||||
|
down_revision = 'ab475332d873'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(engine_name: str) -> None:
|
||||||
|
globals()["upgrade_%s" % engine_name]()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(engine_name: str) -> None:
|
||||||
|
globals()["downgrade_%s" % engine_name]()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('queries',
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('filter_data', dbtables.JSONEncodedDict(), nullable=True),
|
||||||
|
sa.Column('favourite', sa.Boolean(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade_() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('queries')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user