-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Allow per-model template overrides for snippets #10271
Conversation
Manage this branch in SquashTest this branch here: https://laymonagesnippets-per-model-te-ispyf.squash.io |
6dae58b
to
e6b15df
Compare
wagtail/snippets/views/snippets.py
Outdated
def get_template_names(self): | ||
return self.viewset.get_templates( | ||
"revisions_compare", | ||
fallback="wagtailadmin/generic/revisions/compare.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.
Just realised this should be self.template_name
for consistency with the others. Happy to fix this, but I'll hold on in case your review is in progress @gasman.
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.
Not reviewing right now - go ahead!
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! Should be all good to review now.
e6b15df
to
6bd807e
Compare
wagtail/snippets/views/snippets.py
Outdated
@@ -191,17 +191,16 @@ def get_context_data(self, **kwargs): | |||
|
|||
def get_template_names(self): | |||
if self.results_only: | |||
return ["wagtailsnippets/snippets/index_results.html"] | |||
return self.viewset.get_index_results_template() |
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.
Maybe this ship has already sailed and I didn't notice, but I'd very much prefer it if views weren't required to be part of a viewset in order to function. It should always be possible to construct a standalone view independently of any viewset, even if there's not much use for that in practice... as I see it, viewsets are containers for configuration options that are shared between multiple views, and once they've pushed the appropriate bits of configuration to the views at construction time, those views should operate independently from there on. Otherwise, we risk ending up with view logic being spread between views and viewsets in a haphazard way.
I'd suggest that any common code shared by multiple views (such as get_template
here) should be a mixin in wagtail.admin.views.generic.mixins
, and mixed into the relevant 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.
The ship sails from this PR onwards, so as long as this hasn't been merged, there's still time to change the approach 😄
I strongly agree with your points there, and in fact I considered extracting the get_templates()
into a mixin as it makes a lot of sense for this use case. However, this approach doesn't allow you to do the customisation through SnippetViewSet
, so I decided to copy how ModelAdmin does it (the views have self.model_admin
reference).
How do you think such method overrides could be supported? Or do we just not support it, and say that "if you need to do such customisations then you'll need to make subclasses of the view classes with your own get_templates()
, and override all the foo_view_class
attribute on the viewset to use those subclasses"?
Or, do we possibly add something like a SnippetViewSet.get_templates_mixin
and apply the mixin dynamically with type()
, e.g.
@property
def index_view_class(self):
return type('IndexView', (self.get_templates_mixin, IndexView))
So developers can define their own mixin with their own get_templates()
logic, then only swap SnippetViewSet.get_templates_mixin
?
That does mean we'll need to change all the foo_view_class = ...
for the affected views into a @property
, though (no big deal).
Also, does this mean we don't want to implement ModelAdmin's view-specific methods like ModelAdmin.get_index_results_template()
that ModelAdmin supports?
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.
OK, I managed to avoid this pattern by allowing the view's template_name
to be a list. Don't know why Django's TemplateResponseMixin
always forcefully wrap it in a list without checking. Even if get_template_names()
returns a string (instead of a list as the docstring says it must), it still works fine as long as the response_class
resolves it correctly (which SimpleTemplateResponse
does by default).
Anyway, the new approach is much cleaner and lets all the logic be encapsulated within SnippetViewSet
, and all the views know is that the template_name
has been overridden. Using the views without the viewset should still work because they still have the default template_name
s, and they're only overridden through SnippetViewSet
(or if the view is subclassed).
That said, removing the viewset from the views makes it impossible to override things like the get_queryset()
methods on a per-request basis (like ModelAdmin supports), unless we want to pass the methods themselves to the views... 🤔
…tml and index_results.html
…ets ModelIndexView
d52d077
to
b346161
Compare
…late_name to be a list
b346161
to
fcebc47
Compare
fcebc47
to
bf955db
Compare
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.
Very happy with this now - thanks for the updates @laymonage!
This is awesome. Thanks so much @laymonage - great to see one of my favourite ModelAdmin features come across and with a really well documented implementation. |
Part of wagtail/rfcs#85. Incorporates #10256 to make merging easier later.
This allows per-model template overrides for snippets. It works pretty much the same way as ModelAdmin does. Copying the docs:
For all views that are used for a snippet model, Wagtail looks for templates in the following directories within your project or app, before resorting to the defaults:
templates/wagtailsnippets/snippets/{app_label}/{model_name}/
templates/wagtailsnippets/snippets/{app_label}/
templates/wagtailsnippets/snippets/
So, to override the template used by the
IndexView
for example, you could create a newindex.html
template and put it in one of those locations. For example, if you wanted to do this for aShirt
model in ashirts
app, you could add your custom template asshirts/templates/wagtailsnippets/snippets/shirts/shirt/index.html
.For some common views, Wagtail also allows you to override the template used by either specifying the
{view_name}_template_name
attribute or overriding theget_{view_name}_template()
method on the viewset. The following is a list of customisation points for the views:IndexView
:index.html
,index_template_name
, orget_index_template()
index_results.html
,index_results_template_name
, orget_index_results_template()
.CreateView
:create.html
,create_template_name
, orget_create_template()
EditView
:edit.html
,edit_template_name
, orget_edit_template()
DeleteView
:delete.html
,delete_template_name
, orget_delete_template()
HistoryView
:history.html
,history_template_name
, orget_history_template()
Again, docs are still crammed together in that section, I'll try to rewrite it closer to (or during) the feature freeze.
Note: I don't know if we should support this for the chooser views as well. It would be nice to have it, but they currently use the generic templates (except
results.html
), and the base chooser views do not extendTemplateResponseMixin
, so we cannot useget_template_names()
without much refactoring. I'll leave it out of this PR for now.Note 2: I also renamed the index view's template name from
type_index.html
toindex.html
. I think the nametype_index.html
is slightly misleading, since we also have an index view that lists the snippet types.For context, pre-4.0, the snippets type listing (
/admin/snippets/
) uses theindex.html
template, while the listing view for a given model (/admin/snippets/app_label/model_name/
) uses thetype_index.html
. I don't know what the original intention for the names was, but to me it seems like they should've been the other way around.Then, in 4.0, I refactored the type listing view to use the generic
IndexView
(specifically in 4f83cbb), deleting thewagtailsnippets/snippets/index.html
template and reusing thewagtailadmin/generic/index.html
template instead. I didn't bother renamingtype_index.html
toindex.html
because of the potential implications. However, we've had a few releases since then, and I think this PR is a good time to rename it.To re-introduce a simple way to override the type listing template, I made it so that the view looks for
model_index.html
template before resorting toself.template_name
, which is the inheritedwagtailadmin/generic/index.html
. Using the namemodel_index.html
instead oftype_index.html
as it aligns better with the view name (ModelIndexView
) and avoids confusion/conflicts with the previoustype_index.html
template for the actual index view.Please check the following:
make lint
from the Wagtail root.Footnotes
Development Testing ↩