Menu link for BaseView is determined by last @expose method (by alphabetical order), not by explicit identity or first @expose
Checklist
- [ ] The bug is reproducible against the latest release or
master. - [ ] There are no similar issues or pull requests to fix it yet.
Describe the bug
Hello! When using BaseView with multiple methods decorated by @expose, the link that appears in the admin menu is determined by the last method (by alphabetical order) with @expose, regardless of the identity parameter or the order in the class. Expected behavior: The menu link should be determined by either: The first method with @expose (by code order), or The method with a specific identity (if set), or Allow explicit control (e.g. via a class attribute or parameter). Actual behavior: The menu link always points to the endpoint of the last method (by alphabetical order) with @expose. Even if I set identity explicitly in the class or in the decorator, it gets overwritten.
class OrdersView(BaseView):
name = "Orders"
identity = "ordersview"
@expose("/orders", identity="ordersview", methods=["GET"])
async def orders_list(self, request):
...
@expose("/orders/bulk-action", methods=["POST"])
async def orders_bulk_action(self, request):
...
In this case, the menu link points to /orders/bulk-action instead of /orders. Why this is a problem: It is not intuitive and makes it impossible to control which endpoint is shown in the menu without hacks (like renaming methods). Explicit identity is ignored if there are multiple @expose methods. Suggested solution: Assign identity only once (for the first @expose method found), or Allow explicit control via a class attribute or decorator parameter, or Document the current behavior and recommend best practices. Thank you for your work on this great project!
Steps to reproduce the bug
No response
Expected behavior
No response
Actual behavior
No response
Debugging material
No response
Environment
- SQLAdmin version: 0.20.1
- Python version: 3.11
- Database: PostgreSQL 15
- Running inside Docker: yes
- OS: Ubuntu 22.04 (inside container)
Additional context
No response
Same here, spent an hour do understand this)
We often need some API-only exposes in the same view, and current alphabetical-sort behavior is very misleading.
Now the menu item links itself to the first decorated method ALPHABETICALLY
This happens because when calling add_base_view, the decorated functions are registered in alphabetical order and iterated in reverse, so the identity keeps getting overwritten and ultimately the one from the very first function is assigned to the view.
https://github.com/aminalaee/sqladmin/blob/4d230acd80d4016b23159e4249aa507973901b01/sqladmin/application.py#L272-L274
https://github.com/aminalaee/sqladmin/blob/4d230acd80d4016b23159e4249aa507973901b01/sqladmin/application.py#L158-L161
https://github.com/aminalaee/sqladmin/blob/4d230acd80d4016b23159e4249aa507973901b01/sqladmin/application.py#L203-L206
When registering the menu to navbar, it uses the view’s identity, so the menu URL is also set based on that value.
https://github.com/aminalaee/sqladmin/blob/4d230acd80d4016b23159e4249aa507973901b01/sqladmin/_menu.py#L74-L77
This doesn’t occur with ModelView because it is already handled. I’m not sure whether this behavior for BaseView is intended.
A possible fix would be to call view.identity = getattr(func, "_identity") only when the view does not already have an identity, and manually define the identity directly as a static variable on the class.