diff --git a/src/quickbot/bot/handlers/editors/boolean.py b/src/quickbot/bot/handlers/editors/boolean.py index 29584f0..9481dcb 100644 --- a/src/quickbot/bot/handlers/editors/boolean.py +++ b/src/quickbot/bot/handlers/editors/boolean.py @@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder from babel.support import LazyProxy from logging import getLogger -from ....model.descriptors import FieldDescriptor +from ....model.descriptors import BotContext, FieldDescriptor from ....model.user import UserBase from ..context import ContextData, CallbackCommand from ....utils.main import get_send_message @@ -67,12 +67,20 @@ async def bool_editor( state_data = kwargs["state_data"] + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + await wrap_editor( keyboard_builder=keyboard_builder, field_descriptor=field_descriptor, callback_data=callback_data, state_data=state_data, user=user, + context=context, ) state: FSMContext = kwargs["state"] diff --git a/src/quickbot/bot/handlers/editors/date.py b/src/quickbot/bot/handlers/editors/date.py index 040094f..716dcb5 100644 --- a/src/quickbot/bot/handlers/editors/date.py +++ b/src/quickbot/bot/handlers/editors/date.py @@ -6,7 +6,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder from logging import getLogger from typing import TYPE_CHECKING -from ....model.descriptors import FieldDescriptor +from ....model.descriptors import BotContext, FieldDescriptor from ....model.settings import Settings from ....model.user import UserBase from ..context import ContextData, CallbackCommand @@ -158,6 +158,12 @@ async def time_picker( ) state_data = kwargs["state_data"] + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) await wrap_editor( keyboard_builder=keyboard_builder, @@ -165,6 +171,7 @@ async def time_picker( callback_data=callback_data, state_data=state_data, user=user, + context=context, ) await state.set_data(state_data) @@ -272,12 +279,20 @@ async def date_picker( state_data = kwargs["state_data"] + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + await wrap_editor( keyboard_builder=keyboard_builder, field_descriptor=field_descriptor, callback_data=callback_data, state_data=state_data, user=user, + context=context, ) await state.set_data(state_data) @@ -295,11 +310,11 @@ async def date_picker( async def date_picker_year( query: CallbackQuery, callback_data: ContextData, - app: "QBotApp", state: FSMContext, user: UserBase, **kwargs, ): + app: "QBotApp" = kwargs["app"] start_date = datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M") state_data = await state.get_data() @@ -366,12 +381,20 @@ async def date_picker_year( field_descriptor = get_field_descriptor(app, callback_data) + context = BotContext( + db_session=kwargs["db_session"], + app=app, + app_state=kwargs["app_state"], + message=query, + ) + await wrap_editor( keyboard_builder=keyboard_builder, field_descriptor=field_descriptor, callback_data=callback_data, state_data=state_data, user=user, + context=context, ) await query.message.edit_reply_markup(reply_markup=keyboard_builder.as_markup()) @@ -380,9 +403,8 @@ async def date_picker_year( @router.callback_query( ContextData.filter(F.command == CallbackCommand.DATE_PICKER_MONTH) ) -async def date_picker_month( - query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs -): +async def date_picker_month(query: CallbackQuery, callback_data: ContextData, **kwargs): + app: "QBotApp" = kwargs["app"] field_descriptor = get_field_descriptor(app, callback_data) state: FSMContext = kwargs["state"] state_data = await state.get_data() diff --git a/src/quickbot/bot/handlers/editors/entity.py b/src/quickbot/bot/handlers/editors/entity.py index 6fdcc5f..15a1199 100644 --- a/src/quickbot/bot/handlers/editors/entity.py +++ b/src/quickbot/bot/handlers/editors/entity.py @@ -293,12 +293,20 @@ async def render_entity_picker( state_data = kwargs["state_data"] + context = BotContext( + db_session=db_session, + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + await wrap_editor( keyboard_builder=keyboard_builder, field_descriptor=field_descriptor, callback_data=callback_data, state_data=state_data, user=user, + context=context, ) await state.set_data(state_data) diff --git a/src/quickbot/bot/handlers/editors/main.py b/src/quickbot/bot/handlers/editors/main.py index f584a67..fbce44b 100644 --- a/src/quickbot/bot/handlers/editors/main.py +++ b/src/quickbot/bot/handlers/editors/main.py @@ -5,12 +5,13 @@ from aiogram.types import Message, CallbackQuery from logging import getLogger from sqlmodel.ext.asyncio.session import AsyncSession -from quickbot.model.descriptors import EntityForm +from quickbot.model.descriptors import BotContext, EntityForm from ....model import EntityPermission from ....model.settings import Settings from ....model.user import UserBase from ....utils.main import ( + build_field_sequence, check_entity_permission, get_field_descriptor, ) @@ -134,6 +135,22 @@ async def field_editor(message: Message | CallbackQuery, **kwargs): form: EntityForm = entity_descriptor.forms.get( form_name, entity_descriptor.default_form ) + if form.edit_field_sequence: + field_sequence = form.edit_field_sequence + else: + context = BotContext( + db_session=db_session, + app=app, + app_state=kwargs["app_state"], + message=message, + ) + field_sequence = await build_field_sequence( + entity_descriptor=entity_descriptor, + user=user, + callback_data=callback_data, + state_data=state_data, + context=context, + ) entity_data = { key: serialize( getattr( @@ -143,7 +160,7 @@ async def field_editor(message: Message | CallbackQuery, **kwargs): entity_descriptor.fields_descriptors[key], ) for key in ( - form.edit_field_sequence + field_sequence if callback_data.context == CommandContext.ENTITY_EDIT else [callback_data.field_name] ) diff --git a/src/quickbot/bot/handlers/editors/main_callbacks.py b/src/quickbot/bot/handlers/editors/main_callbacks.py index b100a73..9660e87 100644 --- a/src/quickbot/bot/handlers/editors/main_callbacks.py +++ b/src/quickbot/bot/handlers/editors/main_callbacks.py @@ -3,7 +3,7 @@ from aiogram import Router, F from aiogram.types import Message, CallbackQuery from aiogram.fsm.context import FSMContext from sqlmodel.ext.asyncio.session import AsyncSession -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from decimal import Decimal import json @@ -39,6 +39,26 @@ if TYPE_CHECKING: router = Router() +async def _validate_value( + field_descriptor: FieldDescriptor, + value: Any, + message: Message | CallbackQuery, + **kwargs: Any, +) -> bool | str: + if field_descriptor.validator: + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + if iscoroutinefunction(field_descriptor.validator): + return await field_descriptor.validator(value, context) + else: + return field_descriptor.validator(value, context) + return True + + @router.message(CallbackCommandFilter(CallbackCommand.FIELD_EDITOR_CALLBACK)) @router.callback_query( ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR_CALLBACK) @@ -59,6 +79,39 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs): field_descriptor = get_field_descriptor(app, callback_data) type_base = field_descriptor.type_base + if type_base in [int, float, Decimal]: + try: + val = type_base(value) # @IgnoreException + except Exception: + return await message.answer( + text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)) + ) + elif type_base is str and field_descriptor.localizable: + locale_index = int(state_data.get("locale_index")) + val = { + list(LanguageBase.all_members.values())[ + locale_index + ].value: message.text + } + else: + val = value + + validation_result = await _validate_value( + field_descriptor=field_descriptor, + value=val, + message=message, + **kwargs, + ) + + if isinstance(validation_result, str): + return await message.answer( + text=f"{await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)}\n{validation_result}" + ) + elif not validation_result: + return await message.answer( + text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)) + ) + if type_base is str and field_descriptor.localizable: locale_index = int(state_data.get("locale_index")) @@ -90,13 +143,6 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs): **kwargs, ) - elif type_base in [int, float, Decimal]: - try: - _ = type_base(value) # @IgnoreException - except Exception: - return await message.answer( - text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)) - ) else: callback_data: ContextData = kwargs["callback_data"] if callback_data.data: @@ -150,6 +196,9 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs app: "QBotApp" = kwargs["app"] entity_descriptor = get_entity_descriptor(app, callback_data) + entity_data = state_data.get("entity_data", {}) + entity_data[field_descriptor.field_name] = value + if callback_data.context == CommandContext.COMMAND_FORM: field_sequence = list(field_descriptor.command.param_form.keys()) current_index = field_sequence.index(callback_data.field_name) @@ -167,11 +216,18 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs if form.edit_field_sequence: field_sequence = form.edit_field_sequence else: - field_sequence = build_field_sequence( + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + field_sequence = await build_field_sequence( entity_descriptor=entity_descriptor, user=user, callback_data=callback_data, state_data=state_data, + context=context, ) current_index = ( @@ -182,8 +238,6 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs ) field_descriptors = entity_descriptor.fields_descriptors - 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 @@ -224,7 +278,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs ] and current_index < len(field_sequence) - 1 ): - entity_data[field_descriptor.field_name] = value + # entity_data[field_descriptor.field_name] = value state_data.update({"entity_data": entity_data}) next_field_name = field_sequence[current_index + 1] @@ -252,7 +306,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs ) else: - entity_data[field_descriptor.name] = value + # entity_data[field_descriptor.field_name] = value # What if user has several roles and each role has its own ownership field? Should we allow creation even # if user has no CREATE_ALL permission diff --git a/src/quickbot/bot/handlers/editors/string.py b/src/quickbot/bot/handlers/editors/string.py index ad8cbca..81c0dd6 100644 --- a/src/quickbot/bot/handlers/editors/string.py +++ b/src/quickbot/bot/handlers/editors/string.py @@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder from logging import getLogger from typing import Any -from ....model.descriptors import FieldDescriptor +from ....model.descriptors import BotContext, FieldDescriptor from ....model.language import LanguageBase from ....model.settings import Settings from ....model.user import UserBase @@ -87,12 +87,20 @@ async def string_editor( state_data = kwargs["state_data"] + context = BotContext( + db_session=kwargs["db_session"], + app=kwargs["app"], + app_state=kwargs["app_state"], + message=message, + ) + await wrap_editor( keyboard_builder=keyboard_builder, field_descriptor=field_descriptor, callback_data=callback_data, state_data=state_data, user=user, + context=context, ) await state.set_data(state_data) diff --git a/src/quickbot/bot/handlers/editors/wrapper.py b/src/quickbot/bot/handlers/editors/wrapper.py index 4e5da80..29b8c7a 100644 --- a/src/quickbot/bot/handlers/editors/wrapper.py +++ b/src/quickbot/bot/handlers/editors/wrapper.py @@ -2,7 +2,7 @@ from aiogram.types import InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder from ....model.settings import Settings -from ....model.descriptors import EntityForm, FieldDescriptor +from ....model.descriptors import BotContext, EntityForm, FieldDescriptor from ....model.user import UserBase from ..context import ContextData, CallbackCommand, CommandContext from ....utils.navigation import get_navigation_context, pop_navigation_context @@ -15,6 +15,7 @@ async def wrap_editor( callback_data: ContextData, state_data: dict, user: UserBase, + context: BotContext, ): if callback_data.context in [ CommandContext.ENTITY_CREATE, @@ -42,11 +43,12 @@ async def wrap_editor( if form.edit_field_sequence: field_sequence = form.edit_field_sequence else: - field_sequence = build_field_sequence( + field_sequence = await build_field_sequence( entity_descriptor=field_descriptor.entity_descriptor, user=user, callback_data=callback_data, state_data=state_data, + context=context, ) field_index = ( field_sequence.index(field_descriptor.name) diff --git a/src/quickbot/bot/handlers/forms/entity_form.py b/src/quickbot/bot/handlers/forms/entity_form.py index e063827..042dce4 100644 --- a/src/quickbot/bot/handlers/forms/entity_form.py +++ b/src/quickbot/bot/handlers/forms/entity_form.py @@ -208,11 +208,12 @@ async def entity_item( if form.edit_field_sequence: field_sequence = form.edit_field_sequence else: - field_sequence = build_field_sequence( + field_sequence = await build_field_sequence( entity_descriptor=entity_descriptor, user=user, callback_data=callback_data, state_data=state_data, + context=context, ) edit_delete_row.append( InlineKeyboardButton( @@ -314,30 +315,32 @@ async def entity_item( if skip: continue - field_caption = ( - await get_callable_str( - callable_str=field_descriptor.caption, - context=context, - descriptor=field_descriptor, - ) - if field_descriptor.caption - else field_descriptor.field_name - ) if field_descriptor.caption_value: - value = await get_callable_str( - callable_str=field_descriptor.caption_value, - context=context, - descriptor=field_descriptor, - entity=entity_item, - ) + item_text += f"\n{ + await get_callable_str( + callable_str=field_descriptor.caption_value, + context=context, + descriptor=field_descriptor, + entity=entity_item, + ) + }" else: + field_caption = ( + await get_callable_str( + callable_str=field_descriptor.caption, + context=context, + descriptor=field_descriptor, + ) + if field_descriptor.caption + else field_descriptor.field_name + ) value = await get_value_repr( value=getattr(entity_item, field_descriptor.field_name), field_descriptor=field_descriptor, context=context, locale=user.lang, ) - item_text += f"\n{field_caption or field_descriptor.name}:{f' {value}' if value else ''}" + item_text += f"\n{field_caption or field_descriptor.name}:{f' {value}' if value else ''}" context = pop_navigation_context(navigation_stack) if context: diff --git a/src/quickbot/bot/handlers/forms/entity_list.py b/src/quickbot/bot/handlers/forms/entity_list.py index ed816df..6fbc555 100644 --- a/src/quickbot/bot/handlers/forms/entity_list.py +++ b/src/quickbot/bot/handlers/forms/entity_list.py @@ -118,6 +118,12 @@ async def entity_list( ) keyboard_builder = InlineKeyboardBuilder() + context = BotContext( + db_session=db_session, + app=app, + app_state=kwargs["app_state"], + message=message, + ) if ( EntityPermission.CREATE in user_permissions @@ -126,11 +132,12 @@ async def entity_list( if form_item.edit_field_sequence: field_sequence = form_item.edit_field_sequence else: - field_sequence = build_field_sequence( + field_sequence = await build_field_sequence( entity_descriptor=entity_descriptor, user=user, callback_data=callback_data, state_data=kwargs["state_data"], + context=context, ) keyboard_builder.row( InlineKeyboardButton( @@ -206,13 +213,6 @@ async def entity_list( total_pages = 1 page = 1 - context = BotContext( - db_session=db_session, - app=app, - app_state=kwargs["app_state"], - message=message, - ) - for item in items: caption = None diff --git a/src/quickbot/bot/handlers/user_handlers/main.py b/src/quickbot/bot/handlers/user_handlers/main.py index b80352f..1b9dcb5 100644 --- a/src/quickbot/bot/handlers/user_handlers/main.py +++ b/src/quickbot/bot/handlers/user_handlers/main.py @@ -98,6 +98,7 @@ async def cammand_handler(message: Message | CallbackQuery, cmd: BotCommand, **k db_session=kwargs["db_session"], user=kwargs["user"], app=app, + app_state=kwargs["app_state"], state_data=state_data, state=state, i18n=kwargs["i18n"], diff --git a/src/quickbot/main.py b/src/quickbot/main.py index 291a343..e2c20c9 100644 --- a/src/quickbot/main.py +++ b/src/quickbot/main.py @@ -161,6 +161,7 @@ class QBotApp(Generic[UserType, ConfigType], FastAPI): ) else: for locale, description in command.caption.items(): + locale = "default" if locale == "en" else locale if locale not in commands_captions: commands_captions[locale] = [] commands_captions[locale].append((command_name, description)) diff --git a/src/quickbot/model/descriptors.py b/src/quickbot/model/descriptors.py index cc4f835..8aec2af 100644 --- a/src/quickbot/model/descriptors.py +++ b/src/quickbot/model/descriptors.py @@ -31,7 +31,7 @@ class FieldEditButton: @dataclass class CommandButton: - command: ContextData | Callable[[ContextData, Any], ContextData] | str + command: ContextData | Callable[["BotEntity", "BotContext"], ContextData] | str caption: str | LazyProxy | Callable[["BotEntity", "BotContext"], str] | None = None visibility: Callable[["BotEntity", "BotContext"], bool] | None = None @@ -113,6 +113,12 @@ class _BaseFieldDescriptor: is_visible: ( bool | Callable[["FieldDescriptor", "BotEntity", "BotContext"], bool] | None ) = None + is_visible_in_edit_form: ( + bool + | Callable[["FieldDescriptor", Union["BotEntity", Any], "BotContext"], bool] + | None + ) = None + validator: Callable[[Any, "BotContext"], Union[bool, str]] | None = None localizable: bool = False bool_false_value: str | LazyProxy = "no" bool_true_value: str | LazyProxy = "yes" @@ -219,6 +225,7 @@ class CommandCallbackContext[UT: UserBase]: db_session: AsyncSession user: UT app: "QBotApp" + app_state: State state_data: dict[str, Any] state: FSMContext form_data: dict[str, Any] diff --git a/src/quickbot/model/settings.py b/src/quickbot/model/settings.py index ed22011..cd8d26d 100644 --- a/src/quickbot/model/settings.py +++ b/src/quickbot/model/settings.py @@ -2,6 +2,7 @@ from types import NoneType, UnionType from aiogram.utils.i18n.context import get_i18n from datetime import datetime from sqlmodel import SQLModel, Field, select +from sqlmodel.ext.asyncio.session import AsyncSession from typing import Any, get_args, get_origin from ..db import async_session @@ -193,11 +194,23 @@ class Settings(metaclass=SettingsMetaclass): ) @classmethod - async def get[T](cls, param: T, all_locales=False, locale: str = None) -> T: + async def get[T]( + cls, + param: T, + session: AsyncSession = None, + all_locales=False, + locale: str = None, + ) -> T: name = param.field_name if name not in cls._cache.keys(): - cls._cache[name] = await cls.load_param(param) + if session is None: + async with async_session() as session: + cls._cache[name] = await cls.load_param( + session=session, param=param + ) + else: + cls._cache[name] = await cls.load_param(session=session, param=param) ret_val = cls._cache[name] @@ -213,18 +226,17 @@ class Settings(metaclass=SettingsMetaclass): return ret_val @classmethod - async def load_param(cls, param: FieldDescriptor) -> Any: - async with async_session() as session: - db_setting = ( - await session.exec( - select(DbSettings).where(DbSettings.name == param.field_name) - ) - ).first() + async def load_param(cls, session: AsyncSession, param: FieldDescriptor) -> Any: + db_setting = ( + await session.exec( + select(DbSettings).where(DbSettings.name == param.field_name) + ) + ).first() - if db_setting: - return await deserialize( - session=session, type_=param.type_, value=db_setting.value - ) + if db_setting: + return await deserialize( + session=session, type_=param.type_, value=db_setting.value + ) return ( param.default_factory() @@ -240,20 +252,20 @@ class Settings(metaclass=SettingsMetaclass): ) ) - @classmethod - async def load_params(cls): - async with async_session() as session: - db_settings = (await session.exec(select(DbSettings))).all() - for db_setting in db_settings: - if db_setting.name in cls.__dict__: - setting = cls.__dict__[db_setting.name] # type: FieldDescriptor - cls._cache[db_setting.name] = await deserialize( - session=session, - type_=setting.type_, - value=db_setting.value, - ) + # @classmethod + # async def load_params(cls): + # async with async_session() as session: + # db_settings = (await session.exec(select(DbSettings))).all() + # for db_setting in db_settings: + # if db_setting.name in cls.__dict__: + # setting = cls.__dict__[db_setting.name] # type: FieldDescriptor + # cls._cache[db_setting.name] = await deserialize( + # session=session, + # type_=setting.type_, + # value=db_setting.value, + # ) - cls._loaded = True + # cls._loaded = True @classmethod async def set_param(cls, param: str | FieldDescriptor, value) -> None: diff --git a/src/quickbot/utils/main.py b/src/quickbot/utils/main.py index 3fd4393..455a907 100644 --- a/src/quickbot/utils/main.py +++ b/src/quickbot/utils/main.py @@ -15,6 +15,7 @@ from ..model.descriptors import ( FieldDescriptor, EntityDescriptor, EntityPermission, + _BaseFieldDescriptor, ) from ..bot.handlers.context import CallbackCommand, ContextData, CommandContext @@ -198,10 +199,13 @@ async def get_callable_str( if len(args) == 3: return await callable_str(descriptor, entity, context) else: - if issubclass(args[0].annotation, BotEntity): - return await callable_str(entity, context) - else: + param = args[next(iter(args))] + if not isinstance(param.annotation, str) and issubclass( + param.annotation, _BaseFieldDescriptor + ): return await callable_str(descriptor, context) + else: + return await callable_str(entity, context) else: if len(args) == 3: return callable_str(descriptor, entity, context) @@ -245,11 +249,12 @@ def get_field_descriptor( return None -def build_field_sequence( +async def build_field_sequence( entity_descriptor: EntityDescriptor, user: "UserBase", callback_data: ContextData, state_data: dict, + context: BotContext, ): prev_form_list = None @@ -269,47 +274,57 @@ def build_field_sequence( prev_form_name, entity_descriptor.default_list ) + entity_data = state_data.get("entity_data", {}) + field_sequence = list[str]() # exclude ownership fields from edit if user has no CREATE_ALL/UPDATE_ALL permission user_permissions = get_user_permissions(user, entity_descriptor) for fd in entity_descriptor.fields_descriptors.values(): - skip = False - if ( - fd.is_optional - or fd.field_name == "id" - or ( - fd.field_name[-3:] == "_id" - and fd.field_name[:-3] in entity_descriptor.fields_descriptors - ) - or fd.default is not None - or fd.default_factory is not None - ): - skip = True - for own_field in entity_descriptor.ownership_fields.items(): + if isinstance(fd.is_visible_in_edit_form, bool): + skip = not fd.is_visible_in_edit_form + elif callable(fd.is_visible_in_edit_form): + if iscoroutinefunction(fd.is_visible_in_edit_form): + skip = not await fd.is_visible_in_edit_form(fd, entity_data, context) + else: + skip = not fd.is_visible_in_edit_form(fd, entity_data, context) + else: + skip = False if ( - own_field[1].rstrip("_id") == fd.field_name.rstrip("_id") - and own_field[0] in user.roles - and ( - ( - EntityPermission.CREATE_ALL not in user_permissions - and callback_data.context == CommandContext.ENTITY_CREATE - ) - or ( - EntityPermission.UPDATE_ALL not in user_permissions - and callback_data.context == CommandContext.ENTITY_EDIT - ) + fd.is_optional + or fd.field_name == "id" + or ( + fd.field_name[-3:] == "_id" + and fd.field_name[:-3] in entity_descriptor.fields_descriptors ) + or fd.default is not None + or fd.default_factory is not None ): skip = True - break + for own_field in entity_descriptor.ownership_fields.items(): + if ( + own_field[1].rstrip("_id") == fd.field_name.rstrip("_id") + and own_field[0] in user.roles + and ( + ( + EntityPermission.CREATE_ALL not in user_permissions + and callback_data.context == CommandContext.ENTITY_CREATE + ) + or ( + EntityPermission.UPDATE_ALL not in user_permissions + and callback_data.context == CommandContext.ENTITY_EDIT + ) + ) + ): + skip = True + break - if ( - prev_form_list - and prev_form_list.static_filters - and fd.field_name.rstrip("_id") - in [f.field_name.rstrip("_id") for f in prev_form_list.static_filters] - ): - skip = True + if ( + prev_form_list + and prev_form_list.static_filters + and fd.field_name.rstrip("_id") + in [f.field_name.rstrip("_id") for f in prev_form_list.static_filters] + ): + skip = True if not skip: field_sequence.append(fd.field_name)