Implemented Delete feature in DataGridsManager.py. There is still a bug as DBEngine.delete is not implemented

Improved readability for tests using matcher
This commit is contained in:
2026-02-21 18:31:11 +01:00
parent 730f55d65b
commit d447220eae
12 changed files with 460 additions and 49 deletions

View File

@@ -15,7 +15,7 @@ class DataGridsRegistry(SingleInstance):
def put(self, namespace, name, datagrid_id):
"""
:param namespace:
:param name:
:param datagrid_id:
@@ -25,6 +25,18 @@ class DataGridsRegistry(SingleInstance):
all_entries[datagrid_id] = (namespace, name)
self._db_manager.save(DATAGRIDS_REGISTRY_ENTRY_KEY, all_entries)
def remove(self, datagrid_id):
"""
Remove a datagrid from the registry.
Args:
datagrid_id: ID of the datagrid to remove
"""
all_entries = self._db_manager.load(DATAGRIDS_REGISTRY_ENTRY_KEY)
if datagrid_id in all_entries:
del all_entries[datagrid_id]
self._db_manager.save(DATAGRIDS_REGISTRY_ENTRY_KEY, all_entries)
def get_all_tables(self):
all_entries = self._get_all_entries()
return [f"{namespace}.{name}" for (namespace, name) in all_entries.values()]
@@ -45,28 +57,28 @@ class DataGridsRegistry(SingleInstance):
try:
as_fullname_dict = self._get_entries_as_full_name_dict()
grid_id = as_fullname_dict[table_name]
# load dataframe from dedicated store
store = self._db_manager.load(f"{grid_id}#df")
df = store["ne_df"] if store else None
return df[column_name].tolist() if df is not None else []
except KeyError:
return []
def get_row_count(self, table_name):
try:
as_fullname_dict = self._get_entries_as_full_name_dict()
grid_id = as_fullname_dict[table_name]
# load dataframe from dedicated store
store = self._db_manager.load(f"{grid_id}#df")
df = store["ne_df"] if store else None
return len(df) if df is not None else 0
except KeyError:
return 0
def get_column_type(self, table_name, column_name):
"""
Get the type of a column.
@@ -81,11 +93,11 @@ class DataGridsRegistry(SingleInstance):
try:
as_fullname_dict = self._get_entries_as_full_name_dict()
grid_id = as_fullname_dict[table_name]
# load datagrid state
state_id = f"{grid_id}#state"
state = self._db_manager.load(state_id)
if state and "columns" in state:
for col in state["columns"]:
if col.col_id == column_name:

View File

@@ -3,7 +3,8 @@ import inspect
import json
import logging
import uuid
from typing import Optional
from dataclasses import dataclass
from typing import Optional, Literal
from myutils.observable import NotObservableError, ObservableResultCollector
@@ -15,6 +16,19 @@ logger = logging.getLogger("Commands")
AUTO_SWAP_OOB = "__auto_swap_oob__"
@dataclass
class BoundCommand:
"""
Represents a command bound to another command.
Attributes:
command: The command to execute
when: When to execute the bound command ("before" or "after" the main command)
"""
command: 'Command'
when: Literal["before", "after"] = "after"
class Command:
"""
Represents the base command class for defining executable actions.
@@ -128,19 +142,31 @@ class Command:
def execute(self, client_response: dict = None):
logger.debug(f"Executing command {self.name} with arguments {client_response=}")
with ObservableResultCollector(self._bindings) as collector:
# Execute "before" bound commands
ret_from_before_commands = []
if self.owner:
before_commands = [bc for bc in self.owner.get_bound_commands(self.name) if bc.when == "before"]
for bound_cmd in before_commands:
logger.debug(f" will execute bound command {bound_cmd.command.name} BEFORE...")
r = bound_cmd.command.execute(client_response)
ret_from_before_commands.append(r)
# Execute main callback
kwargs = self._create_kwargs(self.default_kwargs,
client_response,
{"client_response": client_response or {}})
ret = self.callback(*self.default_args, **kwargs)
ret_from_bound_commands = []
# Execute "after" bound commands
ret_from_after_commands = []
if self.owner:
for command in self.owner.get_bound_commands(self.name):
logger.debug(f" will execute bound command {command.name}...")
r = command.execute(client_response)
ret_from_bound_commands.append(r) # it will be flatten if needed later
all_ret = flatten(ret, ret_from_bound_commands, collector.results)
after_commands = [bc for bc in self.owner.get_bound_commands(self.name) if bc.when == "after"]
for bound_cmd in after_commands:
logger.debug(f" will execute bound command {bound_cmd.command.name} AFTER...")
r = bound_cmd.command.execute(client_response)
ret_from_after_commands.append(r)
all_ret = flatten(ret, ret_from_before_commands, ret_from_after_commands, collector.results)
# Set the hx-swap-oob attribute on all elements returned by the callback
if self._htmx_extra[AUTO_SWAP_OOB]:

View File

@@ -3,6 +3,7 @@ import uuid
from typing import Optional
from myfasthtml.controls.helpers import Ids
from myfasthtml.core.commands import BoundCommand
from myfasthtml.core.constants import NO_DEFAULT_VALUE
from myfasthtml.core.utils import pascal_to_snake, get_class, snake_to_pascal
@@ -109,9 +110,18 @@ class BaseInstance:
parent = self.get_parent()
return parent.get_full_id() if parent else None
def bind_command(self, command, command_to_bind):
def bind_command(self, command, command_to_bind, when="after"):
"""
Bind a command to another command.
Args:
command: Command name or Command instance to bind to
command_to_bind: Command to execute when the main command is triggered
when: "before" or "after" - when to execute the bound command (default: "after")
"""
command_name = command.name if hasattr(command, "name") else command
self._bound_commands.setdefault(command_name, []).append(command_to_bind)
bound = BoundCommand(command=command_to_bind, when=when)
self._bound_commands.setdefault(command_name, []).append(bound)
def get_bound_commands(self, command_name):
return self._bound_commands.get(command_name, [])