66from datetime import datetime , timedelta , timezone
77from enum import Enum
88import os
9- from flask import current_app
9+ from flask import current_app , abort
1010from flask_sqlalchemy import Model
11+ from marshmallow import Schema
1112from pathvalidate import sanitize_filename
1213from sqlalchemy import Column , JSON
1314from sqlalchemy .sql .elements import UnaryExpression
@@ -33,7 +34,7 @@ def split_order_param(order_param: str) -> Optional[OrderParam]:
3334
3435
3536def get_order_param (
36- cls : Model , order_param : OrderParam , json_sort : dict = None
37+ cls : Model , order_param : OrderParam , json_sort : dict = None , field_map : dict = None
3738) -> Optional [UnaryExpression ]:
3839 """Return order by clause parameter for SQL query
3940
@@ -43,12 +44,18 @@ def get_order_param(
4344 :type order_param: OrderParam
4445 :param json_sort: type mapping for sort by json field, e.g. '{"storage": "int"}', defaults to None
4546 :type json_sort: dict
47+ :param field_map: mapping for translating public field names to internal DB columns, e.g. '{"size": "disk_usage"}'
48+ :type field_map: dict
4649 """
50+ # translate field name to column name
51+ db_column_name = order_param .name
52+ if field_map and order_param .name in field_map :
53+ db_column_name = field_map [order_param .name ]
4754 # find candidate for nested json sort
48- if "." in order_param . name :
49- col , attr = order_param . name .split ("." )
55+ if "." in db_column_name :
56+ col , attr = db_column_name .split ("." )
5057 else :
51- col = order_param . name
58+ col = db_column_name
5259 attr = None
5360 order_attr = cls .__table__ .c .get (col , None )
5461 if not isinstance (order_attr , Column ):
@@ -80,7 +87,9 @@ def get_order_param(
8087 return order_attr .desc ()
8188
8289
83- def parse_order_params (cls : Model , order_params : str , json_sort : dict = None ):
90+ def parse_order_params (
91+ cls : Model , order_params : str , json_sort : dict = None , field_map : dict = None
92+ ) -> list [UnaryExpression ]:
8493 """Convert order parameters in query string to list of order by clauses.
8594
8695 :param cls: Db model class
@@ -89,6 +98,8 @@ def parse_order_params(cls: Model, order_params: str, json_sort: dict = None):
8998 :type order_params: str
9099 :param json_sort: type mapping for sort by json field, e.g. '{"storage": "int"}', defaults to None
91100 :type json_sort: dict
101+ :param field_map: mapping response fields to database column names, e.g. '{"size": "disk_usage"}'
102+ :type field_map: dict
92103
93104 :rtype: List[Column]
94105 """
@@ -97,7 +108,7 @@ def parse_order_params(cls: Model, order_params: str, json_sort: dict = None):
97108 order_param = split_order_param (p )
98109 if not order_param :
99110 continue
100- order_attr = get_order_param (cls , order_param , json_sort )
111+ order_attr = get_order_param (cls , order_param , json_sort , field_map )
101112 if order_attr is not None :
102113 order_by_params .append (order_attr )
103114 return order_by_params
@@ -135,3 +146,16 @@ def save_diagnostic_log_file(app: str, username: str, body: bytes) -> str:
135146 f .write (content )
136147
137148 return file_name
149+
150+
151+ def get_schema_fields_map (schema : Schema ) -> dict :
152+ """
153+ Creates a mapping of schema field names to corresponding DB columns.
154+ This allows sorting by the API field name (e.g. 'size') while
155+ actually sorting by the database column (e.g. 'disk_usage').
156+ """
157+ mapping = {}
158+ for name , field in schema ._declared_fields .items ():
159+ if field and field .attribute :
160+ mapping [name ] = field .attribute
161+ return mapping
0 commit comments