Saving changes now that most things have been migrated to CRUD V3 and appear to be working. This still needs testing though.

This commit is contained in:
Scott Idem
2026-01-06 13:11:03 -05:00
parent 552ca31603
commit 9c06b07665
6 changed files with 310 additions and 795 deletions

View File

@@ -723,7 +723,7 @@ def sql_select(
sql_search_qry = '' sql_search_qry = ''
if search_query: if search_query:
log.info('Creating partial SQL string for complex SearchQuery.') log.info('Creating partial SQL string for complex SearchQuery.')
sql_search_qry, data_search = sql_search_qry_part(search_query, searchable_fields=searchable_fields) sql_search_qry, data_search = sql_search_qry_part(search_query, searchable_fields=searchable_fields, table_name=table_name)
data = {**data, **data_search} data = {**data, **data_search}
sql = text( sql = text(
@@ -830,7 +830,7 @@ def sql_select(
sql_search_qry = '' sql_search_qry = ''
if search_query: if search_query:
log.info('Creating partial SQL string for complex SearchQuery.') log.info('Creating partial SQL string for complex SearchQuery.')
sql_search_qry, data_search = sql_search_qry_part(search_query, searchable_fields=searchable_fields) sql_search_qry, data_search = sql_search_qry_part(search_query, searchable_fields=searchable_fields, table_name=table_name)
data = {**data, **data_search} data = {**data, **data_search}
# # NOTE: Version 3 of the fulltext search # # NOTE: Version 3 of the fulltext search
@@ -2052,7 +2052,7 @@ def sql_enable_part(table_name: str, enabled: str) -> bool|dict:
sql = f'AND `{table_name}`.enable = false' sql = f'AND `{table_name}`.enable = false'
enable = False enable = False
elif enabled == 'all': elif enabled == 'all':
sql = f'AND (`{table_name}`.enable = true OR `{table_name}`.enable = false OR `{table_name}`.enable IS NULL)' sql = ''
enable = None enable = None
log.debug(sql) log.debug(sql)
@@ -2080,7 +2080,7 @@ def sql_hidden_part(table_name: str, hidden: str) -> bool|dict:
sql = f'AND (`{table_name}`.hide = false OR `{table_name}`.hide IS NULL)' sql = f'AND (`{table_name}`.hide = false OR `{table_name}`.hide IS NULL)'
hide = False hide = False
elif hidden == 'all': elif hidden == 'all':
sql = f'AND (`{table_name}`.hide = true OR `{table_name}`.hide = false OR `{table_name}`.hide IS NULL)' sql = ''
hide = None hide = None
log.debug(sql) log.debug(sql)
@@ -2109,11 +2109,13 @@ def sql_limit_offset_part(limit: int, offset: int = 0) -> bool|str:
# ### BEGIN ### API DB SQL Methods ### sql_search_qry_part() ### # ### BEGIN ### API DB SQL Methods ### sql_search_qry_part() ###
# NEW 2026-01-02 # NEW 2026-01-02
# Updated to support complex POST-based searches with recursive logical grouping. # Updated to support complex POST-based searches with recursive logical grouping.
# Updated 2026-01-06 to handle missing default_qry_str column gracefully.
@logger_reset @logger_reset
def sql_search_qry_part( def sql_search_qry_part(
search_query: Any, # SearchQuery model instance search_query: Any, # SearchQuery model instance
searchable_fields: List[str]|None = None, # List of allowed fields searchable_fields: List[str]|None = None, # List of allowed fields
max_depth: int = 5, # Maximum recursion depth max_depth: int = 5, # Maximum recursion depth
table_name: str|None = None, # Target table for schema validation
) -> tuple[str, dict]: ) -> tuple[str, dict]:
""" """
Recursively builds a SQL WHERE clause from a SearchQuery model. Recursively builds a SQL WHERE clause from a SearchQuery model.
@@ -2157,9 +2159,35 @@ def sql_search_qry_part(
# Process 'query_string' (Standardized Full-Text Search) # Process 'query_string' (Standardized Full-Text Search)
if hasattr(query_node, 'query_string') and query_node.query_string: if hasattr(query_node, 'query_string') and query_node.query_string:
p_name = get_param_name() if query_node.query_string == '%':
clauses.append(f"MATCH( default_qry_str ) AGAINST( :{p_name} IN BOOLEAN MODE )") # Wildcard: Skip filtering for this part
data[p_name] = query_node.query_string pass
else:
# Check if default_qry_str exists in this table/view
use_match = True
if table_name:
try:
db.execute(text(f"SELECT default_qry_str FROM `{table_name}` LIMIT 0"))
except:
use_match = False
else:
use_match = False # Safe default if no table_name
if use_match:
p_name = get_param_name()
clauses.append(f"MATCH( default_qry_str ) AGAINST( :{p_name} IN BOOLEAN MODE )")
data[p_name] = query_node.query_string
elif searchable_fields:
# Fallback: OR LIKE across all searchable fields
like_clauses = []
for field in searchable_fields:
# Skip internal/numeric fields for full-text-like search
if not any(x in field for x in ['_id', 'enable', 'hide', 'priority', 'sort', 'group', 'created_on', 'updated_on']):
f_p_name = get_param_name()
like_clauses.append(f"`{field}` LIKE :{f_p_name}")
data[f_p_name] = f"%{query_node.query_string}%"
if like_clauses:
clauses.append(f"({' OR '.join(like_clauses)})")
# Process 'and' filters # Process 'and' filters
if hasattr(query_node, 'and_filters') and query_node.and_filters: if hasattr(query_node, 'and_filters') and query_node.and_filters:

View File

@@ -95,4 +95,5 @@ class Account_Base(BaseModel):
class Config: class Config:
underscore_attrs_are_private = True underscore_attrs_are_private = True
fields = base_fields fields = base_fields
allow_population_by_field_name = True
# ### END ### API Account Models ### Account_Base() ### # ### END ### API Account Models ### Account_Base() ###

View File

@@ -1,6 +1,7 @@
from app.models.event_models import * from app.models.event_models import *
from app.models.event_file_models import * from app.models.event_file_models import *
from app.models.event_device_models import * from app.models.event_device_models import *
from app.models.event_cfg_models import *
events_general_obj_li = { events_general_obj_li = {
'event': { 'event': {
@@ -91,4 +92,22 @@ events_general_obj_li = {
'enable', 'hide', 'priority', 'group', 'notes', 'created_on', 'updated_on' 'enable', 'hide', 'priority', 'group', 'notes', 'created_on', 'updated_on'
], ],
}, },
'event_cfg': {
'tbl': 'event_cfg',
'tbl_default': 'event_cfg',
'tbl_update': 'event_cfg',
'mdl': Event_Cfg_Base,
'mdl_default': Event_Cfg_Base,
'mdl_in': Event_Cfg_Base,
'mdl_out': Event_Cfg_Base,
# Legacy V2 keys:
'table_name': 'event_cfg',
'tbl_name_update': 'event_cfg',
'base_name': Event_Cfg_Base,
# V3 Search Security:
'searchable_fields': [
'event_cfg_id_random', 'event_id_random', 'enable', 'conference',
'status', 'hide', 'priority', 'group', 'notes'
],
},
} }

View File

@@ -3,6 +3,7 @@ from app.models.event_badge_template_models import *
from app.models.event_person_models import * from app.models.event_person_models import *
from app.models.event_person_tracking_models import * from app.models.event_person_tracking_models import *
from app.models.event_registration_models import * from app.models.event_registration_models import *
from app.models.event_person_profile_models import *
events_registration_obj_li = { events_registration_obj_li = {
'event_badge': { 'event_badge': {
@@ -66,6 +67,28 @@ events_registration_obj_li = {
'notes', 'created_on', 'updated_on' 'notes', 'created_on', 'updated_on'
], ],
}, },
'event_person_profile': {
'tbl': 'event_person_profile',
'tbl_default': 'v_event_person_profile',
'tbl_update': 'event_person_profile',
'mdl': Event_Person_Profile_Base,
'mdl_default': Event_Person_Profile_Base,
'mdl_in': Event_Person_Profile_Base,
'mdl_out': Event_Person_Profile_Base,
# Legacy V2 keys:
'table_name': 'v_event_person_profile',
'tbl_name_update': 'event_person_profile',
'base_name': Event_Person_Profile_Base,
# V3 Search Security:
'searchable_fields': [
'event_person_profile_id_random', 'account_id_random',
'contact_id_random', 'event_id_random', 'event_person_id_random',
'organization_id_random', 'pronouns', 'informal_name', 'given_name',
'family_name', 'professional_title', 'full_name', 'affiliations',
'email', 'enable', 'priority', 'group', 'notes', 'created_on',
'updated_on'
],
},
'event_person_tracking': { 'event_person_tracking': {
'tbl': 'event_person_tracking', 'tbl': 'event_person_tracking',
'tbl_default': 'v_event_person_tracking', 'tbl_default': 'v_event_person_tracking',

View File

@@ -1,5 +1,7 @@
from app.models.order_models import * from app.models.order_models import *
from app.models.order_cart_models import * from app.models.order_cart_models import *
from app.models.product_models import *
from app.models.order_cfg_models import *
order_obj_li = { order_obj_li = {
'order': { 'order': {
@@ -73,4 +75,42 @@ order_obj_li = {
'enable', 'hide', 'priority', 'group', 'created_on', 'updated_on' 'enable', 'hide', 'priority', 'group', 'created_on', 'updated_on'
], ],
}, },
'product': {
'tbl': 'product',
'tbl_default': 'v_product',
'tbl_update': 'product',
'mdl': Product_Base,
'mdl_default': Product_Base,
'mdl_in': Product_Base,
'mdl_out': Product_Base,
# Legacy V2 keys:
'table_name': 'v_product',
'tbl_name_update': 'product',
'base_name': Product_Base,
# V3 Search Security:
'searchable_fields': [
'product_id_random', 'account_id_random', 'for_type', 'for_id_random',
'type_code', 'type_name', 'name', 'description', 'unit_price',
'tax_rate', 'vat_rate', 'max_quantity', 'recurring', 'enable',
'hide', 'priority', 'group', 'created_on', 'updated_on'
],
},
'order_cfg': {
'tbl': 'order_cfg',
'tbl_default': 'order_cfg',
'tbl_update': 'order_cfg',
'mdl': Order_Cfg_Base,
'mdl_default': Order_Cfg_Base,
'mdl_in': Order_Cfg_Base,
'mdl_out': Order_Cfg_Base,
# Legacy V2 keys:
'table_name': 'order_cfg',
'tbl_name_update': 'order_cfg',
'base_name': Order_Cfg_Base,
# V3 Search Security:
'searchable_fields': [
'account_id_random', 'account_name', 'default_no_reply_email',
'confirm_email'
],
},
} }

File diff suppressed because it is too large Load Diff