plain-templates changelog
0.4.0 (2026-06-07)
What's changed
CreateView,UpdateView, andDeleteViewadapt to the newModelForm.create()/update()split (plain-postgres0.107.0).CreateView.form_validnow callsform.create();UpdateView.form_validnow callsform.update()and assigns the result toself.object(it previously leftself.objectunset). (66634f5af9)DeleteViewno longer routes deletion through the form. ItsEmptyDeleteFormused to take the instance and define asave()that deleted it; it is now a plain fieldless confirmationForm, andDeleteView.form_validcallsself.object.delete()directly. Theget_form_kwargsoverride that injectedinstanceis removed. (66634f5af9)
Upgrade instructions
- If you subclass
CreateVieworUpdateViewand overrideform_validto callform.save(), switch toform.create()/form.update(). Using these views with aModelFormrequiresplain-postgres>=0.107.0(which providescreate()/update()). - If you subclass
DeleteViewand relied onEmptyDeleteFormaccepting aninstancekwarg or itssave()method, note the form is now fieldless and deletion happens inform_validviaself.object.delete()— overrideform_validfor custom delete behavior.
0.3.0 (2026-05-16)
What's changed
TemplateView.render_template()replaced by `render(context).** The new method returns aResponse(not astr) and layers any keyword context overget_template_context(), so a handler can push what the template needs straight in —self.render(product=product)— instead of stashing it onselfforget_template_context()to read back. Called with no arguments it rendersget_template_context()as-is, which is whatget()` does. (d88e0556b0)FormView.form_invalidremoved.post()now re-renders an invalid form directly viaself.render(form=form);form_validis unchanged. (ddbbfb05dc)
Upgrade instructions
- Replace
render_template()withrender(). Sincerender()returns aResponse,Response(self.render_template())becomesself.render(). Per-render context can be passed as keyword arguments (self.render(form=form)) instead of going throughget_template_context(). - If you overrode
FormView.form_invalid, move that logic into arender()orpost()override — the invalid-form path now callsself.render(form=form).
0.2.0 (2026-05-13)
What's changed
TemplateView.handle_exceptionrenders{status}.html. The error-template handling that lived inplaincore's exception handler now sits onTemplateViewitself:404.htmlforNotFoundError404,500.htmlfor unhandled errors, etc., with context{request, status_code, exception, DEBUG}. Falls through to plain-text rendering (viaraise exc from None) onTemplateFileMissing; logs and returns a bare-statusResponseon any other render failure so_respond_to_exceptioncan still attachresponse.exception. (90d8fd983b)- New
NotFoundViewcatchall view.before_requestraisesNotFoundError404before method dispatch, so every HTTP method renders the styled 404 instead of falling through to a 405. Mount it as the last route —path("<path:_>", NotFoundView)— for a styled 404 on unmatched URLs. Pairs with the catchall route semantics added inplain0.145.0. (90d8fd983b)
Upgrade instructions
If you have a
404.htmltemplate, mountNotFoundViewas the last route — otherwise unmatched URLs now return plain-text404 Not Foundinstead of rendering your template. URL-resolution failures happen before any view runs, so plain core's exception handler can't reachTemplateView.handle_exception; the catchall route is what gives those requests aTemplateViewto render through.from plain.templates.views import NotFoundView from plain.urls import Router, path class AppRouter(Router): urls = [ # ... your routes ... path("<path:_>", NotFoundView), ]Apps that previously relied on plain core auto-rendering
{status}.htmlwill still see those templates render for exceptions raised inside a view —TemplateView.handle_exceptionfires whenever an exception escapes aTemplateViewsubclass. For pre-view failures (URL resolution, middleware) plain core stays in plain text unless you mountNotFoundView(above) or otherwise route through aTemplateView.
0.1.0 (2026-05-12)
What's changed
- First release.
plain.templatesis now a separate package, extracted fromplaincore (19b622a7ca). It owns:- The Jinja2 engine,
DefaultEnvironment, theTEMPLATES_JINJA_ENVIRONMENTsetting TemplateandTemplateFileMissingregister_template_global,register_template_filter,register_template_extension- The template-rendering view bases:
TemplateView,FormView,DetailView,CreateView,UpdateView,DeleteView,ListViewatplain.templates.views - The
{status_code}.htmlerror rendering wired throughplaincore's framework default exception handler (lazy import — degrades to plain text if this package isn't installed)
- The Jinja2 engine,
Upgrade instructions
- Install the package and add it to
INSTALLED_PACKAGES— see theplain0.143.0 release notes for the full migration.