-
Notifications
You must be signed in to change notification settings - Fork 32
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
RFC 85: Snippets parity with ModelAdmin #85
RFC 85: Snippets parity with ModelAdmin #85
Conversation
a8518ce
to
79043f1
Compare
|
||
Snippets currently have no such configuration, as it only registers one "Snippets" top-level menu item. The only possible customisation is to hide the top-level menu item through the `construct_main_menu` hook. Adding a menu item for each snippet needs to be done separately using the `register_admin_menu_item` hook. | ||
|
||
If we want to support this, there are a few questions to answer: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discuss
|
||
We have added support for custom icons in Snippets in [#10178][10178] using the `SnippetViewSet.icon` attribute. The icon is used throughout the admin views, so it makes sense to reuse it for the menu item. | ||
|
||
If desired, we could also add a `menu_icon` attribute to `SnippetViewSet` to allow customising the menu icon separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discuss, though I personally don't see much value for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The menu_icon
attribute in ModelAdmin serves the same purpose really (it isn't just for the 'menu' - it appears in headers and everywhere else icons are featured), so I would class this as done. Icons are all about improving recognisably, so it's difficult to imagine wanting a separate icon to appear in the menu.
- [ ] **`ModelAdmin.empty_value_display`** | ||
|
||
String to display in place of empty values (e.g. `None`, `""`, `[]`). Defaults to `"-"`. | ||
|
||
- [ ] **`ModelAdmin.get_empty_value_display()`** | ||
|
||
As with `empty_value_display`, but can return different strings based on the `field_name`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this? We're currently using the tables component from wagtail.admin.ui.tables
, so adding these may be slightly tricky. I'd imagine we could add an additional keyword argument to the base Column
class for the empty value display and pass the value from SnippetViewSet
there. However, we have quite a lot of keyword arguments on the Column
class already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I added these to ModelAdmin for parity with Django, but I haven't ever used them in a project. These days, if I wanted to change the blank/null representation for a specific field, I'd be more likely to add a property method to the model class and use that instead.
|
||
Same as `list_display`, but for exporting to spreadsheets. | ||
|
||
- [ ] **`ModelAdmin.list_filter`** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be marked as done, or do we want to do something more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's definitely scope for something more convenient than the django-filter
approach, but that would probably fair better as a separate RFC, as the aim should be for a consistant approach throughout.
|
||
The name of the exported spreadsheet. | ||
|
||
- [ ] **`ModelAdmin.search_fields`** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think it is not worth it, but feel free to discuss
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think a like-for-like implementation would be desirable either. Though, having some control over which fields to search on when searching from the listing page could definitely be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I noticed that Wagtail's generic IndexView
already has search_fields
in place that we could hook up from SnippetViewSet
.
I also managed to add support to switch between Wagtail's search backend or Django's QuerySet API in a minimal way that doesn't need a separate search handler class. I believe the main use cases for ModelAdmin's search handler is to choose between Wagtail search and Django anyway, so I think this should be enough for our parity goal.
See:
### Template overrides | ||
|
||
By default, ModelAdmin looks in these directories for template discovery: | ||
|
||
1. `templates/modeladmin/app-name/model-name/` | ||
2. `templates/modeladmin/app-name/` | ||
3. `templates/modeladmin/` | ||
|
||
Which allows template overrides without specifying `foo_template_name` in the `ModelAdmin` class. | ||
|
||
This is currently unsupported in Snippets. Adding the support in Snippets seems feasible, and it would be useful for customising the templates without having to specify the template names in the `SnippetViewSet`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the current templates live in templates/wagtailsnippets/snippets/
, the likely discovery order would be:
templates/wagtailsnippets/snippets/app-name/model-name/
templates/wagtailsnippets/snippets/app-name/
templates/wagtailsnippets/snippets/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just found myself wanting to override a template to get some custom JS into a chooser's 'create' view and this feature would definitely be useful. I don't suppose the paths matter too much (as long as they are documented), but I would have thought that wagtailsnippets/
instead of wagtailsnippets/snippets/
would be verbose enough to avoid clashes with anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, this was an old diff of the RFC and the feature was implemented in wagtail/wagtail#10271 (available in 5.0+).
wagtailsnippets/snippets/
was kept for backwards-compatibility, but it can be customised via SnippetViewSet.template_prefix
. The RFC has been updated to reflect this.
### Helper classes | ||
|
||
Helper classes encapsulate the logic that is commonly used across views in ModelAdmin. There are three types of helper classes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal opinion is to not implement these for snippets, as we can achieve the same things via other means that are more in line with how we do things in Wagtail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fundamentally, I agree. Helper classes were just my way of encapsulating certain bits of logic in a way that made things portable/swappable, without complicating the ModelAdmin class too much (something I'd always felt Django's version could do better).
It feels as though Wagtail has naturally evolved to do much of this in different ways, which is fine. I think it's fair to say there are some rough edges as a result of that though, that will need addressing at some point (for example, the title
column in snippet listings always adding an edit link, regardless of whether the user has that permission). The route to overriding permission checking logic via generic views in general feels like it needs some refinement, but that doesn't necessarily need to be part of this RFC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, thanks, I'll try to revisit this in a future release.
- **Shall we allow partial registration, i.e. CRUD without chooser and vice versa?** | ||
|
||
There has been requests for the ability to register snippets without having the chooser registered, and vice versa, register the chooser but without the CRUD views. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discuss
As noted in wagtail/wagtail#10206 (reply in thread), I think one way we could allow disabling this is by overriding the {foo}_view_class
(or the {foo}_view
) properties on the SnippetViewSet
to None
. Then, in get_urlpatterns()
, we only register the URLs if the corresponding view is not None
.
As noted in that comment, though, the tricky part is to gracefully handle the UI when the expected URLs are not available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For all it's sins, ModelAdmin does at least have this element down, but the recommended solution would be to use a custom PermissionHelper
class to block things at the permission-checking level (e.g. By always returning False
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think this would be possible to some extent via the permission_policy
that is customisable on SnippetViewSet
, but I haven't tested this. Will revisit in a future release.
- **Shall we consider changing the signature of `register_snippet`?** | ||
|
||
Currently, `register_snippet` takes a model class as its first argument, while the `viewset` is an optional second argument. With ModelAdmin, only the `ModelAdmin` class is passed to `modeladmin_register` function, and the model class is retrieved from the `model` attribute of the `ModelAdmin` class. | ||
|
||
We could consider doing the same for Snippets. However, to avoid this being a breaking change, we could still support a model class as a first argument by checking whether it's a model class or a `SnippetViewSet` class and do the registration accordingly. | ||
|
||
Changing the signature of `register_snippet` would also allow us to accept a group class as described in [Customising the menu item](#customising-the-menu-item). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat in favour of allowing both register_snippet(SnippetModel)
and register_snippet(SnippetViewSet)
, with the option to deprecate the viewset
parameter in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that flexibility is a nice improvement on what modeladmin has.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, again this has been implemented in wagtail/wagtail@8df6e62 and available in Wagtail 5.0+.
- **What is the deprecation timeline of ModelAdmin?** | ||
|
||
We should consider how long we want to keep ModelAdmin as a contrib module before detaching it and publishing it as an external package. Before the app is detached, we should also provide a migration path for users who want to migrate from ModelAdmin to Snippets. Once the app is detached, we could leave a stub page in the ModelAdmin documentation that links to the external package. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should probably answer this myself, but I wonder what others might think about it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't think we should bother breaking modeladmin out into it's own package - the maintenence overhead wouldn't really justify the effort of removing it in the first place. I think it'd be quite reasonable to deprecate it without any fixed timeline for removal, encourage users to feed back on any pain points that come with migration, and just remove it once we feel ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the modeladmin package as serving a similar role to the hallo.js one - a courtesy for people who are "locked in" to modeladmin, perhaps because they've made low-level customisations or simply because they don't have a developer available for the task of figuring out the correspondences from their modeladmin code to snippets. Keeping it outside Wagtail core will communicate that new projects shouldn't use it (and third-party packages that depend on it should migrate away). We only need to maintain it to the extent that the demand from the community exists, and over time the need for that package will organically fade away.
Just wanted to say wow! Thanks @laymonage - this is an incredible write up and covers the discussion of every single feature I can think of so far. I've had a quick read but will double back with comments when I have a bit more time. One thing that could be clearer is that Snippets are intended to become the recommended way to manage non-Page models within Wagtail. You have mentioned that there is a seperate RFC for managing other views of Pages which is good. However, I think this is something that will intentionally be different compared to ModelAdmin. |
Since ModelAdmin has relatively unique API names, particularly for all the customisable attributes, I had a go at checking how many of the projects I have access to use those APIs. Here are the results: ModelAdmin API usage - RFC 85 ModelAdmin vs. Snippets feature parity. This is for 70 "Torchbox" website/client projects, and 70 "Wagtail" packages (excluding Wagtail itself). Those projects aren’t particularly representative but I hope this nonetheless helps illustrate how much usage those APIs might get relative to one-another. Summary: Things that show up a lot (some of which could be false positives): Things that have 0 use across all 140 projects:
Things that see lots of usage in TBX sites/projects, very little in Wagtail packages:
Things that see lots of usage in Wagtail packages, little on TBX projects:
|
5c851c3
to
5d36445
Compare
Hi all, I've updated the RFC to reflect the current state of the parity and our plans for the deprecation timeline. Please leave a comment if you have any feedback, thanks! |
|
||
A dict mapping prepopulated fields to a tuple of fields to prepopulate from, useful for slug generation on Page models. | ||
|
||
This feature uses a custom JavaScript file that concatenates and slugifies the values of the specified fields into a target field. The `TitleFieldPanel` added in [#10568][10568] demonstrates a different approach to this feature that builds on top of Wagtail's Panels API and it can be used for Snippets. We can expand the new approach to support more generic use cases via Stimulus in the future. Thus, we will not support this attribute. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be good to call out there are already issues to do similar sets of behaviour and it is best we solve this with either documented example Stimulus approaches or Wagtail wide solutions such as via Panels as mentioned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I've updated this paragraph to add this detail.
|
||
- Turn off specific views | ||
|
||
There have also been requests to turn off specific views. While it is possible to override the URL registration of the views, the current views implementation rely on the other views being available. For example, the ability to turn off the index, create, and delete views can be useful to enforce a singleton snippet within the admin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d like to see us move the documentation for a custom clean method to somewhere else if possible.
Either to a Snippets tips & tricks or somewhere else suitable, this is a common request and the functionality is actually something generic to all forms in Wagtail and is often something asked about on Slack.
https://docs.wagtail.org/en/stable/reference/contrib/modeladmin/tips_and_tricks/custom_clean.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, I'll try to do this during the 5.1 feature freeze. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incredible @laymonage
I was quite skeptical when this was discussed over a year ago as ModelAdmin is one of my favourite features. However, I feel that the approach taken has been smooth, steady and offers a much better API surface area that also makes sense in the context of the rest of Wagtail.
I think we should do our best to ensure that the split out ModelAdmin repo has some nightly tests against Wagtail main, at least for one LTS release cycle. So that the core team / others can try to keep compatibility with this external package while the community migrates.
It may be worth also reviewing what other key community packages are reliant on ModelAdmin and alert them with an issue on their repo about this RFC once approved. This may already be done.
Happy to approve this in its current state and very glad to see this all worked through in such great detail.
5d36445
to
b16d7c8
Compare
Thank you everyone for all the comments! As this has been approved for a while and we've made a lot of progress in the Wagtail repo, I'm going to merge this just in time for Wagtail 5.1 RC release. |
Rendered
Follow up to the Snippets vs ModelAdmin parity discussion, as I think using an RFC is easier for reviewing each individual customisation point described in the discussion.
Note that some of the items in the RFC have been implemented and merged into
main
(PRs are linked). However, it is likely that some of the customisations need a clearer implementation plan, hence the RFC. These features may not make it into the next release (5.0 at the time of writing), so this RFC can still be useful for future reference.I highly recommend giving the code of the current
SnippetViewSet
class a read: https://github.com/wagtail/wagtail/blob/main/wagtail/snippets/views/snippets.py#L585