Skip to content

Refactor: Class-based views - Eligibility index #2913

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

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benefits/eligibility/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
app_name = "eligibility"
urlpatterns = [
# /eligibility
path("", views.index, name=routes.name(routes.ELIGIBILITY_INDEX)),
path("", views.IndexView.as_view(), name=routes.name(routes.ELIGIBILITY_INDEX)),
path("start", views.start, name=routes.name(routes.ELIGIBILITY_START)),
path("confirm", views.confirm, name=routes.name(routes.ELIGIBILITY_CONFIRM)),
path("unverified", views.unverified, name=routes.name(routes.ELIGIBILITY_UNVERIFIED)),
Expand Down
68 changes: 39 additions & 29 deletions benefits/eligibility/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,63 @@
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.decorators import decorator_from_middleware
from django.views.generic import FormView

from benefits.routes import routes
from benefits.core import recaptcha, session
from benefits.core.middleware import AgencySessionRequired, RecaptchaEnabled, FlowSessionRequired
from benefits.core.mixins import AgencySessionRequiredMixin, RecaptchaEnabledMixin
from benefits.core.models import EnrollmentFlow
from . import analytics, forms, verify

TEMPLATE_CONFIRM = "eligibility/confirm.html"


@decorator_from_middleware(AgencySessionRequired)
@decorator_from_middleware(RecaptchaEnabled)
def index(request):
class IndexView(AgencySessionRequiredMixin, RecaptchaEnabledMixin, FormView):
"""View handler for the enrollment flow selection form."""
agency = session.agency(request)
session.update(request, eligible=False, origin=agency.index_url)

# clear any prior OAuth token as the user is choosing their desired flow
# this may or may not require OAuth, with a different set of scope/claims than what is already stored
session.logout(request)
template_name = "eligibility/index.html"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this template name be defined on line 19, under TEMPLATE_CONFIRM, for consistency?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these global variables for template names were an artifact of having view functions. With class-based views, one of the main benefits is that everything is more cohesive and together, so I don't think we need to keep using these global variables for template names.

form_class = forms.EnrollmentFlowSelectionForm

context = {"form": forms.EnrollmentFlowSelectionForm(agency=agency)}
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = super().get_form_kwargs()
kwargs["agency"] = self.agency
return kwargs

if request.method == "POST":
form = forms.EnrollmentFlowSelectionForm(data=request.POST, agency=agency)
def dispatch(self, request, *args, **kwargs):
"""Initialize session state before handling the request."""

if form.is_valid():
flow_id = form.cleaned_data.get("flow")
flow = EnrollmentFlow.objects.get(id=flow_id)
session.update(request, flow=flow)
# Run super.dispatch() here to
# Ensure all mixins run before dispatch logic
# so that self.agency is available from AgencySessionRequiredMixin
response = super().dispatch(request, *args, **kwargs)

analytics.selected_flow(request, flow)
session.update(request, eligible=False, origin=self.agency.index_url)
session.logout(request)

eligibility_start = reverse(routes.ELIGIBILITY_START)
response = redirect(eligibility_start)
else:
# form was not valid, allow for correction/resubmission
if recaptcha.has_error(form):
messages.error(request, "Recaptcha failed. Please try again.")
context["form"] = form
context.update(agency.eligibility_index_context)
response = TemplateResponse(request, "eligibility/index.html", context)
else:
context.update(agency.eligibility_index_context)
response = TemplateResponse(request, "eligibility/index.html", context)
return response

def form_valid(self, form):
"""If the form is valid, set enrollment flow and redirect."""
flow_id = form.cleaned_data.get("flow")
flow = EnrollmentFlow.objects.get(id=flow_id)
session.update(self.request, flow=flow)

analytics.selected_flow(self.request, flow)
return redirect(reverse(routes.ELIGIBILITY_START))

def form_invalid(self, form):
"""If the form is invalid, display error messages."""
if recaptcha.has_error(form):
messages.error(self.request, "Recaptcha failed. Please try again.")
return super().form_invalid(form)

return response
def get_context_data(self, **kwargs):
"""Add agency-specific context data."""
context = super().get_context_data(**kwargs)
context.update(self.agency.eligibility_index_context)
return context


@decorator_from_middleware(AgencySessionRequired)
Expand Down
4 changes: 2 additions & 2 deletions tests/pytest/eligibility/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def test_index_get_agency_multiple_flows(mocker, model_TransitAgency, model_Enro
response = client.get(path)

assert response.status_code == 200
assert response.template_name == "eligibility/index.html"
assert "eligibility/index.html" in response.template_name
assert "form" in response.context_data
assert isinstance(response.context_data["form"], EnrollmentFlowSelectionForm)

Expand All @@ -143,7 +143,7 @@ def test_index_get_agency_single_flow(mocker, model_TransitAgency, model_Enrollm
response = client.get(path)

assert response.status_code == 200
assert response.template_name == "eligibility/index.html"
assert "eligibility/index.html" in response.template_name
assert "form" in response.context_data
assert isinstance(response.context_data["form"], EnrollmentFlowSelectionForm)

Expand Down
Loading