-
Notifications
You must be signed in to change notification settings - Fork 188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorrect URLs with multiple views on the same model #681
Comments
You are right in most of the assumptions, and yes this usage is not intended, so I think you have two options:
|
I see; I might try to create a custom view in the future. The elements available in the default templates (like the list view) are quite powerful, it would be nice to be able to reuse them, and have some examples in the documentation on how to do so.
In the actual application I would still go with a different view, mostly because the UI displayed for the filtered items is quite different. For context, the table would contain a list of items, some of them in a "pending" state, and most of them in a "processed". The few pending items would have a few actions available on them and the ability to modify them, while the many processed items are there for later double-check and consultation, and are read-only. There are many ways to go about this, including creating two different tables, however the first attempt I made was to produce two different filtered views of the same table. |
I faced the same requirement. I can propose next solution: - cls.identity = slugify_class_name(model.__name__)
+ cls.identity = kwargs.get("identity", slugify_class_name(model.__name__)) So the usage is:
|
I had a ton of trouble with this. I finally found a workaround: create a new inherited class. E.g.:
Now all URLs will work correctly in view2 because it has a brand new No use of |
@MitPitt Thanks! Your workaround is much neater than mine |
Any suggestions how to implement this to avoid such problems? Open to ideas and PRs. |
If The example I wrote above can be written in one line:
I want this to be the equivalent of:
And if no If 2 views have the same Currently |
Not sure if this is what @MitPitt suggested, but could we not make the changes in def _build_url_for(self, name: str, request: Request, obj: Any) -> URL:
return request.url_for(
name,
- identity=slugify_class_name(obj.__class__.__name__),
+ identity=request.path_params.get("identity", slugify_class_name(obj.__class__.__name__)),
pk=get_object_identifier(obj),
) I'm happy to make the PR if it makes sense. |
I think you can give it a try, reproducing it should be simple with this example: #681 (comment) |
Yes I noticed the same thing when running some tests (I myself does not use relationship-models). To solve the problem with the identity being derived from the model-name, I think we need to have a similar solution to what @glarione suggested and in that case, you need to create the second view-class like : class BadDomainUserAdmin(ModelView, model=User, identity="bad_user"):
column_list = [User.id, User.name]
name = "Bad user" # omitting this leads to a TypeError
def list_query(self, request: Request) -> Select:
return select(User).where(User.email == "demo2@bad_domain.org") Otherwise the The other way to do it (and what I have done so far) is to create a "baseview" with the model and basic-settings you want and then inherit that view like: class UserAdmin(ModelView, model=User):
column_list = [User.id, User.name]
class InheritedUserAdmin(UserAdmin):
column_list = [User.name, User.email]
name = "inherited" # omitting this leads to a TypeError
name_plural = "inherited users" # to have a different name in the sidebar
identity = "inherited"
def list_query(self, request: Request) -> Select:
return select(User).where(User.email == "demo2@bad_domain.org") I filed a PR #759 as a start with some changes that addresses these problems. |
Thanks for the initiative, but I'm still not sure if this is the best way to tackle this. Even with this PR, it's still needs some hacking to make it work and that definitely is not ideal. Django for example simply raises an error and doesn't allow registering a model twice. I'm interested to see what is the flask-admin behaviour in this case. Maybe it's still better to implement filters so one can filter records instead of providing two views. |
I agree the identity-hack probably should be solved some other way, removing it from PR. As the PR is now, it works if you work with inheritance, if inheriting the view. This way (with inherited views like above) also works in flask-admin. Flask-admin has the flask-admin: admin.add_view(UserAdmin(User, db.session, name="List", endpoint="list"))
admin.add_view(InheritedUserAdmin(User, db.session, name="Bad User List", endpoint="baduser")) vs in sql-admin: admin.add_view(UserAdmin)
admin.add_view(InheritedUserAdmin) |
Interesting, it is kind of similar to the identity we have, we just get it in a different way, but probably flask-admin is storing the endpoint:model mapping somewhere so it can do the mapping. I think the same could be achieved here, but we should also check how the URL building is done from one model to the other in flask admin. For example we could do: class UserAdmin(ModelView, model=User, identity="user"): ...
class AnotherUserAdmin(ModelView, model=User, identity="another_user"): ...
class AddressAdmin(ModelView, model=Address, identity="address"): ...
# register ... How would the URL from Address know to which User view point to. |
Flask Admin registers one blueprint per view. The model is stored in the view and each model view has its own endpoint functions, which they have access the the model, finally these endpoint functions are registered to the blueprint on the view.
On the other hand, Flask AppBuilder builds the urls almost as is ( At #77 I proposed using Router per view and moving edit, create, detail, etc. endpoints to the I believe using Flask Admin approach over I saw this I would like to knock what is the motivation behind this approach? |
Enable creating multiple views on the same model. See aminalaee#681
Checklist
master
.Describe the bug
The usage case is to provide two different views with
list_query
and slightly different templates.Having two views on the same model requires to provide a different
name
andidentity
to the second view to avoid a crash.However, both links in the menu point to the same
ModelView
, so only one of the two views is usable.Side note: if a
name
is omitted in the second view, we get aTypeError
inMenu.add
(called fromBaseAdmin._build_menu
), which could probably be caught in advance to yield a more meaningful exception, such as "Multiple views on the same model must have different names".Steps to reproduce the bug
MVE:
Expected behavior
There should be two distincts views with distincts URLs (or at least, I didn't find anything suggesting that this in not supported).
Actual behavior
Both menu entries lead to
/admin/user/list
, instead of/admin/bad_user/list
.Debugging material
This seems to be due to
identity
being overwritten by the slugified model name:sqladmin/sqladmin/models.py
Line 89 in 751329c
Changing the line to
seems to fix it at least at first glance, however,
identity
is referenced both in the templates and inmodels.py
as a parameter to build urls, so this is not sufficient. Indeed, the urls for user edititing and detailed display both point touser
instead ofbad_user
.Environment
Additional context
The intention was to provide a customized list view for certain items in the table (so, listing only, no detail/edit view) with a custom form widget to allow the user to perform an action on some of these items.
The text was updated successfully, but these errors were encountered: