Make it a decorator. E.g. the @login_required
decorator you may have used is backed by user_passes_test()
; your @otp_required
decorator could have more logic and only defer to the original view function once your OTP magic passes.
Something like this, as semi-pseudocode. There's no special magic to it, really – the view function decorator simply wraps the view, so it can do whatever a view can.
from functools import wraps
class OtpView(views.FormView):
pass
def requires_otp(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
if not has_valid_otp_for_request(request):
return OtpView.as_view()(request)
return view_func(request, *args, **kwargs)
return _wrapped_view
# ...
@requires_otp
def my_view(...):
pass
# or for CBVs:
... requires_otp(MyView.as_view()) ...