updated Mongo Postgres Redis Controllers
This commit is contained in:
285
Controllers/Postgres/filter.py
Normal file
285
Controllers/Postgres/filter.py
Normal file
@@ -0,0 +1,285 @@
|
||||
"""
|
||||
Advanced filtering functionality for SQLAlchemy models.
|
||||
|
||||
This module provides a comprehensive set of filtering capabilities for SQLAlchemy models,
|
||||
including pagination, ordering, and complex query building.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import arrow
|
||||
|
||||
from typing import Any, TypeVar, Type, Union, Optional
|
||||
|
||||
from sqlalchemy import ColumnExpressionArgument
|
||||
from sqlalchemy.orm import Query, Session
|
||||
from sqlalchemy.sql.elements import BinaryExpression
|
||||
|
||||
from response import PostgresResponse
|
||||
|
||||
|
||||
T = TypeVar("T", bound="QueryModel")
|
||||
|
||||
|
||||
class QueryModel:
|
||||
|
||||
__abstract__ = True
|
||||
pre_query = None
|
||||
|
||||
@classmethod
|
||||
def _query(cls: Type[T], db: Session) -> Query:
|
||||
"""Returns the query to use in the model."""
|
||||
return cls.pre_query if cls.pre_query else db.query(cls)
|
||||
|
||||
@classmethod
|
||||
def add_new_arg_to_args(
|
||||
cls: Type[T],
|
||||
args_list: tuple[BinaryExpression, ...],
|
||||
argument: str,
|
||||
value: BinaryExpression
|
||||
) -> tuple[BinaryExpression, ...]:
|
||||
"""
|
||||
Add a new argument to the query arguments if it doesn't exist.
|
||||
|
||||
Args:
|
||||
args_list: Existing query arguments
|
||||
argument: Key of the argument to check for
|
||||
value: New argument value to add
|
||||
|
||||
Returns:
|
||||
Updated tuple of query arguments
|
||||
"""
|
||||
# Convert to set to remove duplicates while preserving order
|
||||
new_args = list(dict.fromkeys(
|
||||
arg for arg in args_list
|
||||
if isinstance(arg, BinaryExpression)
|
||||
))
|
||||
|
||||
# Check if argument already exists
|
||||
if not any(
|
||||
getattr(getattr(arg, "left", None), "key", None) == argument
|
||||
for arg in new_args
|
||||
):
|
||||
new_args.append(value)
|
||||
|
||||
return tuple(new_args)
|
||||
|
||||
@classmethod
|
||||
def get_not_expired_query_arg(
|
||||
cls: Type[T],
|
||||
args: tuple[BinaryExpression, ...]
|
||||
) -> tuple[BinaryExpression, ...]:
|
||||
"""
|
||||
Add expiry date filtering to the query arguments.
|
||||
|
||||
Args:
|
||||
args: Existing query arguments
|
||||
|
||||
Returns:
|
||||
Updated tuple of query arguments with expiry filters
|
||||
|
||||
Raises:
|
||||
AttributeError: If model does not have expiry_starts or expiry_ends columns
|
||||
"""
|
||||
try:
|
||||
current_time = str(arrow.now())
|
||||
starts = cls.expiry_starts <= current_time
|
||||
ends = cls.expiry_ends > current_time
|
||||
|
||||
args = cls.add_new_arg_to_args(args, "expiry_ends", ends)
|
||||
args = cls.add_new_arg_to_args(args, "expiry_starts", starts)
|
||||
return args
|
||||
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"Model {cls.__name__} must have expiry_starts and expiry_ends columns"
|
||||
) from e
|
||||
|
||||
@classmethod
|
||||
def produce_query_to_add(cls: Type[T], filter_list, args):
|
||||
"""
|
||||
Adds query to main filter options
|
||||
Args:
|
||||
filter_list: Dictionary containing query parameters
|
||||
args: Existing query arguments to add to
|
||||
|
||||
Returns:
|
||||
Updated query arguments tuple
|
||||
"""
|
||||
if filter_list.get("query"):
|
||||
for smart_iter in cls.filter_expr(**filter_list["query"]):
|
||||
if key := getattr(getattr(smart_iter, "left", None), "key", None):
|
||||
args = cls.add_new_arg_to_args(args, key, smart_iter)
|
||||
return args
|
||||
|
||||
@classmethod
|
||||
def convert(
|
||||
cls: Type[T],
|
||||
smart_options: dict[str, Any],
|
||||
validate_model: Any = None
|
||||
) -> Optional[tuple[BinaryExpression, ...]]:
|
||||
"""
|
||||
Convert smart options to SQLAlchemy filter expressions.
|
||||
|
||||
Args:
|
||||
smart_options: Dictionary of filter options
|
||||
validate_model: Optional model to validate against
|
||||
|
||||
Returns:
|
||||
Tuple of SQLAlchemy filter expressions or None if validation fails
|
||||
"""
|
||||
if validate_model is not None:
|
||||
# Add validation logic here if needed
|
||||
pass
|
||||
|
||||
return tuple(cls.filter_expr(**smart_options))
|
||||
|
||||
@classmethod
|
||||
def filter_by_one(
|
||||
cls: Type[T],
|
||||
db: Session,
|
||||
system: bool = False,
|
||||
**kwargs: Any
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter single record by keyword arguments.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
system: If True, skip status filtering
|
||||
**kwargs: Filter criteria
|
||||
|
||||
Returns:
|
||||
Query response with single record
|
||||
"""
|
||||
if "is_confirmed" not in kwargs and not system:
|
||||
kwargs["is_confirmed"] = True
|
||||
kwargs.pop("system", None)
|
||||
query = cls._query(db).filter_by(**kwargs)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_one(
|
||||
cls: Type[T],
|
||||
*args: Union[BinaryExpression, ColumnExpressionArgument],
|
||||
db: Session,
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter single record by expressions.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
*args: Filter expressions
|
||||
|
||||
Returns:
|
||||
Query response with single record
|
||||
"""
|
||||
args = cls.get_not_expired_query_arg(args)
|
||||
query = cls._query(db).filter(*args)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_one_system(
|
||||
cls: Type[T],
|
||||
*args: Union[BinaryExpression, ColumnExpressionArgument],
|
||||
db: Session,
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter single record by expressions without status filtering.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
*args: Filter expressions
|
||||
|
||||
Returns:
|
||||
Query response with single record
|
||||
"""
|
||||
query = cls._query(db).filter(*args)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_all_system(
|
||||
cls: Type[T],
|
||||
*args: Union[BinaryExpression, ColumnExpressionArgument],
|
||||
db: Session,
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter multiple records by expressions without status filtering.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
*args: Filter expressions
|
||||
|
||||
Returns:
|
||||
Query response with matching records
|
||||
"""
|
||||
query = cls._query(db).filter(*args)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_all(
|
||||
cls: Type[T],
|
||||
*args: Union[BinaryExpression, ColumnExpressionArgument],
|
||||
db: Session,
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter multiple records by expressions.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
*args: Filter expressions
|
||||
|
||||
Returns:
|
||||
Query response with matching records
|
||||
"""
|
||||
args = cls.get_not_expired_query_arg(args)
|
||||
query = cls._query(db).filter(*args)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_by_all_system(
|
||||
cls: Type[T],
|
||||
db: Session,
|
||||
**kwargs: Any
|
||||
) -> PostgresResponse[T]:
|
||||
"""
|
||||
Filter multiple records by keyword arguments.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
**kwargs: Filter criteria
|
||||
|
||||
Returns:
|
||||
Query response with matching records
|
||||
"""
|
||||
query = cls._query(db).filter_by(**kwargs)
|
||||
return PostgresResponse(
|
||||
model=cls,
|
||||
pre_query=cls._query(db),
|
||||
query=query,
|
||||
is_array=True
|
||||
)
|
||||
Reference in New Issue
Block a user