Typically such patterns are defined in class-based views where one can use inheritance to override only some parts of the code flow.
Class-based views
Your view for example looks quite similar to a FormView [Django-doc]. Indeed, we can define a view with:
# app_name/views.py
from django.urls import reverse_lazy
from django.views.generic import FormView
class MyView(FormView):
template_name = 'my/template.html'
success_url = reverse_lazy('another_page')
form_class = MyForm
def form_valid(self, form):
do_somethin()
and_something_else()
return super().form_valid(form)
Here the class-based view has implemented a handler for a POST request that will first construct the form, then check if that form is valid, and if it is invalid rerender the template with the form that now contains the errors. It thus minimizes the amount of code that one has to write to handle a simple form.
Such views also explain clearly what they are doing. A CreateView for example explains by its name that it is used to create new objects, it will thus trigger the .save() method of the form it is operating on. A DeleteView on the other hand makes it clear that a POST request to that view will delete an object.
You can also override the attributes in the urls.py by specifying a value in the .as_view(…) [Django-doc] call:
# app_name/urls.py
from app_name.views import MyView
from django.urls import path
urlpatterns = [
# ⋮,
path('some/path/', MyView(template_name='other/template.html'), name='some-name'),
# ⋮
]
A class-based function thus acts as a template method pattern
Decorators
Another way to do this is to work with decorators that implement small pieces of logic that run before/after a call to the function-based view, and can alter the request/response, or decide to raise an error instead of calling the view.
For example the @require_http_methods(…) decorator [Django-doc] [GitHub] is implemented to first check if the method is one of the listed ones. This is implemented as:
def require_http_methods(request_method_list):
# ⋮
def decorator(func):
@wraps(func)
def inner(request, *args, **kwargs):
if request.method not in request_method_list:
response = HttpResponseNotAllowed(request_method_list)
log_response(
'Method Not Allowed (%s): %s', request.method, request.path,
response=response,
request=request,
)
return response
return func(request, *args, **kwargs)
return inner
return decorator
here the decorator thus checks if the request.method is a member of the request_method_list. If that is not the case, it will return a HTTP 405 response, and specify that that method is not allowed.
While Django offers a lot of decorators, most decorators have a mixin counterpart for a class-based views, and some are implemented already in the View class. For example if the View does not contains a get method, then it will return a HTTP 405 response, so here the required_http_method is not needed as a mixin/decorator for a class-based view.
Skeleton functions
You can implement a view function and use parameters instead to pass values. Usually however this will not be as flexible as a class-based view: it is rather easy to pass some parameters, but it is less useful to specify behavior in a pattern: in that case you need to pass a reference to a function, but then the question arises what parameters should be passed to that.
For example if we want to make a function that renders the template, we can work with a view that looks like:
from django.shortcuts import render
def render_some_template(request, parameter, context_generator, template='our/template.html'):
context = context_generator()
return render(request, template, context)
but perhaps the context_generator function should be called together with the parameter? or perhaps know what template will be rendered. This is one of the reasons why altering code flow is usually done with a class-based view, and less with a function-based view.
While a function-based view can work with a skeleton function, it is usually less exendible than the class-based counterpart. Django's builtin apps (the ones defined in the django.contrib module) are moving mainly towards class-based views, since it is easier to extend these.
FormView.