@@ -131,6 +131,7 @@ def __init__(
131131 # Plugins emitting their own row keys extend this set.
132132 self .allowed_framework_keys : set [str ] = {
133133 "_row_actions" ,
134+ "_row_class" ,
134135 "_children" ,
135136 "_sbadmin_tree_last_child" ,
136137 }
@@ -521,20 +522,27 @@ def get_data(self, page_num=None, page_size=None, additional_filter=None):
521522
522523 data_qs = self .build_final_data_queryset (page_num , page_size , additional_filter )
523524 data = list (data_qs )
524-
525- self .process_final_data (data )
526525 request = self .threadsafe_request
527526 plugins = list (request .request_data .configuration .plugins )
528527 for plugin in plugins :
529- data = plugin .modify_final_data (
528+ data = plugin .modify_raw_data (
530529 self ,
531530 request = request ,
532531 data = data ,
533532 )
534533
535534 raw_rows_by_pk = {row [self .get_pk_field ().name ]: dict (row ) for row in data }
535+ self .inject_row_class (data )
536+ self .process_final_data (data )
536537 self .inject_row_actions (data , raw_rows_by_pk = raw_rows_by_pk )
537538
539+ for plugin in plugins :
540+ data = plugin .modify_final_data (
541+ self ,
542+ request = request ,
543+ data = data ,
544+ )
545+
538546 return {
539547 "last_page" : math .ceil (total_count / page_size ),
540548 "data" : data ,
@@ -569,6 +577,21 @@ def process_final_data(self, final_data: list[dict[str, Any]]) -> None:
569577 value = escape (value )
570578 row [field_key ] = value
571579
580+ def inject_row_class (self , final_data : list [dict [str , Any ]]) -> None :
581+ """Attach a ``_row_class`` CSS string to each row, computed from the RAW
582+ row dict (must run before ``process_final_data`` formats column values).
583+
584+ Not every view that uses this action inherits ``SBAdminBaseListView``
585+ (e.g. integration admins), so the hook is resolved defensively and rows
586+ are left untouched when it is absent."""
587+ row_class_hook = getattr (self .view , "get_sbadmin_list_row_class" , None )
588+ if not callable (row_class_hook ):
589+ return
590+ for row in final_data :
591+ if "_row_class" in row :
592+ continue
593+ row ["_row_class" ] = row_class_hook (self .threadsafe_request , row ) or ""
594+
572595 def inject_row_actions (
573596 self , final_data : list [dict [str , Any ]], raw_rows_by_pk : dict | None = None
574597 ) -> None :
@@ -577,6 +600,8 @@ def inject_row_actions(
577600 return
578601 pk_field = self .get_pk_field ().name
579602 for row in final_data :
603+ if "_row_actions" in row :
604+ continue
580605 obj_id = row .get (pk_field )
581606 raw_row = (raw_rows_by_pk or {}).get (obj_id )
582607 row ["_row_actions" ] = [
@@ -641,6 +666,7 @@ def _materialize_row_action(
641666 "css_class" : action .get_css_class (action_row ) or "" ,
642667 "open_in_modal" : bool (action .open_in_modal ),
643668 "is_method_action" : bool (action .action_id ) and not action .open_in_modal ,
669+ "is_download" : bool (getattr (action , "is_download" , False )),
644670 "open_in_new_tab" : bool (action .open_in_new_tab ),
645671 }
646672 if action .sub_actions :
0 commit comments