fix: default values in subordinate entities based on static_filtering, add: button visibility callbacks in entities' forms

This commit is contained in:
Alexander Kalinovsky
2025-01-22 20:04:32 +01:00
parent 9dd0708a5b
commit b40e588379
7 changed files with 129 additions and 77 deletions

View File

@@ -54,6 +54,9 @@ async def time_picker(
): ):
keyboard_builder = InlineKeyboardBuilder() keyboard_builder = InlineKeyboardBuilder()
if not current_value:
current_value = time(0, 0)
for i in range(12): for i in range(12):
keyboard_builder.row( keyboard_builder.row(
InlineKeyboardButton( InlineKeyboardButton(

View File

@@ -167,6 +167,36 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
entity_data = state_data.get("entity_data", {}) entity_data = state_data.get("entity_data", {})
if callback_data.context == CommandContext.ENTITY_CREATE:
stack = state_data.get("navigation_stack", [])
prev_callback_data = ContextData.unpack(stack[-1]) if stack else None
if (
prev_callback_data
and prev_callback_data.command == CallbackCommand.ENTITY_LIST
and prev_callback_data.entity_name == entity_descriptor.name
):
prev_form_name = (
prev_callback_data.form_params.split("&")[0]
if prev_callback_data.form_params
else "default"
)
prev_form_params = (
prev_callback_data.form_params.split("&")[1:]
if prev_callback_data.form_params
else []
)
prev_form_list = entity_descriptor.lists.get(
prev_form_name or "default", entity_descriptor.default_list
)
for filt in prev_form_list.static_filters:
if filt.value_type == "const":
entity_data[filt.field_name] = filt.value
elif len(prev_form_params) > filt.param_index:
entity_data[filt.field_name] = prev_form_params[
filt.param_index
]
if ( if (
callback_data.context callback_data.context
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT] in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]

View File

@@ -84,11 +84,15 @@ async def entity_item(
callback_data.form_params or "default", entity_descriptor.default_form callback_data.form_params or "default", entity_descriptor.default_form
) )
if can_edit:
for edit_buttons_row in form.form_buttons: for edit_buttons_row in form.form_buttons:
btn_row = [] btn_row = []
for button in edit_buttons_row: for button in edit_buttons_row:
if isinstance(button, FieldEditButton):
if button.visibility and not button.visibility(entity_item):
continue
if isinstance(button, FieldEditButton) and can_edit:
field_name = button.field_name field_name = button.field_name
btn_caption = button.caption btn_caption = button.caption
if field_name in entity_descriptor.fields_descriptors: if field_name in entity_descriptor.fields_descriptors:
@@ -114,7 +118,14 @@ async def entity_item(
}" }"
else: else:
btn_text = ( btn_text = (
f"✏️ {get_callable_str(field_descriptor.caption, field_descriptor, entity_item, field_value)}" f"✏️ {
get_callable_str(
field_descriptor.caption,
field_descriptor,
entity_item,
field_value,
)
}"
if field_descriptor.caption if field_descriptor.caption
else f"✏️ {field_name}" else f"✏️ {field_name}"
) )
@@ -132,6 +143,7 @@ async def entity_item(
) )
elif isinstance(button, CommandButton): elif isinstance(button, CommandButton):
btn_caption = button.caption btn_caption = button.caption
if btn_caption: if btn_caption:
btn_text = get_callable_str( btn_text = get_callable_str(
@@ -139,18 +151,22 @@ async def entity_item(
) )
else: else:
btn_text = button.command btn_text = button.command
btn_row.append(
InlineKeyboardButton( if isinstance(button.context_data, ContextData):
text=btn_text, btn_cdata = button.context_data
callback_data=( elif callable(button.context_data):
button.context_data.pack() btn_cdata = button.context_data(callback_data, entity_item)
if button.context_data else:
else ContextData( btn_cdata = ContextData(
command=CallbackCommand.USER_COMMAND, command=CallbackCommand.USER_COMMAND,
user_command=button.command, user_command=button.command,
data=str(entity_item.id), data=str(entity_item.id),
).pack() )
),
btn_row.append(
InlineKeyboardButton(
text=btn_text,
callback_data=btn_cdata.pack(),
) )
) )

View File

@@ -45,6 +45,9 @@ async def entities_menu(
entity_metadata = app.entity_metadata entity_metadata = app.entity_metadata
for entity in entity_metadata.entity_descriptors.values(): for entity in entity_metadata.entity_descriptors.values():
if entity.show_in_entities_menu:
if entity.full_name_plural.__class__ == EntityCaptionCallable: if entity.full_name_plural.__class__ == EntityCaptionCallable:
caption = entity.full_name_plural(entity) or entity.name caption = entity.full_name_plural(entity) or entity.name
elif entity.full_name_plural.__class__ == LazyProxy: elif entity.full_name_plural.__class__ == LazyProxy:

View File

@@ -258,7 +258,7 @@ class BotEntity[CreateSchemaType: BaseModel, UpdateSchemaType: BaseModel](
condition = column(sfilt.field_name).isnot(None) condition = column(sfilt.field_name).isnot(None)
else: else:
condition = None condition = None
if condition: if condition is not None:
select_statement = select_statement.where(condition) select_statement = select_statement.where(condition)
return select_statement return select_statement

View File

@@ -24,6 +24,7 @@ EntityFieldCaptionCallable = Callable[["EntityFieldDescriptor", Any, Any], str]
@dataclass @dataclass
class FieldEditButton: class FieldEditButton:
field_name: str field_name: str
visibility: Callable[[Any], bool] | None = None
caption: str | LazyProxy | EntityFieldCaptionCallable | None = None caption: str | LazyProxy | EntityFieldCaptionCallable | None = None
@@ -31,7 +32,8 @@ class FieldEditButton:
class CommandButton: class CommandButton:
command: str command: str
caption: str | LazyProxy | EntityItemCaptionCallable | None = None caption: str | LazyProxy | EntityItemCaptionCallable | None = None
context_data: ContextData | None = None visibility: Callable[[Any], bool] | None = None
context_data: ContextData | Callable[[ContextData, Any], ContextData] | None = None
@dataclass @dataclass
@@ -64,7 +66,7 @@ class EntityList:
item_form: str | None = None item_form: str | None = None
pagination: bool = True pagination: bool = True
static_filters: list[Filter] | Any = None static_filters: list[Filter] | Any = None
filtering: bool = True filtering: bool = False
filtering_fields: list[str] = None filtering_fields: list[str] = None
order_by: str | Any | None = None order_by: str | Any | None = None

View File

@@ -57,12 +57,10 @@ async def deserialize[T](session: AsyncSession, type_: type[T], value: str = Non
elif type_ is datetime: elif type_ is datetime:
if is_optional and not value: if is_optional and not value:
return None return None
if value[-3] == ":": if value[-3] == "-":
return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
elif value[-3] == "-":
return datetime.strptime(value, "%Y-%m-%d %H-%M") return datetime.strptime(value, "%Y-%m-%d %H-%M")
else: else:
raise ValueError("Invalid datetime format") return datetime.fromisoformat(value)
elif type_ is bool: elif type_ is bool:
return value == "True" return value == "True"
elif type_ is Decimal: elif type_ is Decimal: