Implement view logic

This commit is contained in:
Tobias Kunze 2025-12-02 16:25:16 +01:00
parent ef4f76b290
commit 2a63677539

View file

@ -14,6 +14,7 @@ from servala.core.models import (
ServiceOffering,
)
from servala.frontend.forms.service import (
ComputePlanSelectionForm,
ControlPlaneSelectForm,
ServiceFilterForm,
ServiceInstanceDeleteForm,
@ -152,6 +153,13 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
control_plane=self.selected_plane, service_offering=self.object
).first()
@cached_property
def plan_form(self):
data = self.request.POST if self.request.method == "POST" else None
return ComputePlanSelectionForm(
data=data, control_plane_crd=self.context_object, prefix="plans"
)
def get_instance_form_kwargs(self, ignore_data=False):
return {
"initial": {
@ -205,6 +213,7 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
context["select_form"] = self.select_form
context["has_control_planes"] = self.planes.exists()
context["selected_plane"] = self.selected_plane
context["context_object"] = self.context_object
context["hide_expert_mode"] = self.hide_expert_mode
if self.request.method == "POST":
if self.is_custom_form:
@ -222,6 +231,17 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
if self.selected_plane and self.selected_plane.wildcard_dns:
context["wildcard_dns"] = self.selected_plane.wildcard_dns
context["organization_namespace"] = self.request.organization.namespace
if self.context_object:
context["plan_form"] = self.plan_form
context["has_available_plans"] = self.plan_form.fields[
"compute_plan_assignment"
].queryset.exists()
if self.context_object.control_plane.storage_plan_price_per_gib:
context["storage_plan"] = {
"price_per_gib": self.context_object.control_plane.storage_plan_price_per_gib,
}
return context
def post(self, request, *args, **kwargs):
@ -232,6 +252,9 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
context["form_error"] = True
return self.render_to_response(context)
if not self.plan_form.is_valid():
return self.render_to_response(context)
if self.is_custom_form:
form = self.get_custom_instance_form()
else:
@ -245,7 +268,11 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
)
return self.render_to_response(context)
if form.is_valid():
if form.is_valid() and self.plan_form.is_valid():
compute_plan_assignment = self.plan_form.cleaned_data[
"compute_plan_assignment"
]
try:
service_instance = ServiceInstance.create_instance(
organization=self.request.organization,
@ -253,16 +280,22 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
context=self.context_object,
created_by=request.user,
spec_data=form.get_nested_data().get("spec"),
compute_plan_assignment=compute_plan_assignment,
)
return redirect(service_instance.urls.base)
except ValidationError as e:
form.add_error(None, e.message or str(e))
except Exception as e:
error_message = self.organization.add_support_message(
_(f"Error creating instance: {str(e)}.")
_("Error creating instance: {error}.").format(error=str(e))
)
form.add_error(None, error_message)
if self.is_custom_form:
context["custom_service_form"] = form
else:
context["service_form"] = form
return self.render_to_response(context)
@ -332,6 +365,18 @@ class ServiceInstanceDetailView(
context["has_delete_permission"] = self.request.user.has_perm(
ServiceInstance.get_perm("delete"), self.object
)
if self.object.compute_plan_assignment:
context["compute_plan_assignment"] = self.object.compute_plan_assignment
if (
self.object.context
and self.object.context.control_plane.storage_plan_price_per_gib
):
context["storage_plan"] = {
"price_per_gib": self.object.context.control_plane.storage_plan_price_per_gib,
}
return context
def get_nested_spec(self):
@ -475,6 +520,17 @@ class ServiceInstanceUpdateView(
kwargs.pop("data", None)
return cls(**kwargs)
@cached_property
def plan_form(self):
data = self.request.POST if self.request.method == "POST" else None
initial = self.object.compute_plan_assignment if self.object else None
return ComputePlanSelectionForm(
data=data,
control_plane_crd=self.object.context if self.object else None,
prefix="plans",
initial={"compute_plan_assignment": initial} if initial else None,
)
@property
def is_custom_form(self):
# Note: "custom form" = user-friendly, subset of fields
@ -489,7 +545,7 @@ class ServiceInstanceUpdateView(
else:
form = self.get_form()
if form.is_valid():
if form.is_valid() and self.plan_form.is_valid():
return self.form_valid(form)
return self.form_invalid(form)
@ -506,14 +562,29 @@ class ServiceInstanceUpdateView(
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["hide_expert_mode"] = self.hide_expert_mode
# Check if a form was passed (e.g., from form_invalid)
form_from_kwargs = kwargs.get("form")
if self.request.method == "POST":
if self.is_custom_form:
context["custom_form"] = self.get_custom_form()
# Use the form with errors if passed, otherwise create new
context["custom_form"] = form_from_kwargs or self.get_custom_form()
context["form"] = self.get_form(ignore_data=True)
else:
# Use the form with errors if passed, otherwise create new
context["form"] = form_from_kwargs or self.get_form()
context["custom_form"] = self.get_custom_form(ignore_data=True)
else:
context["custom_form"] = self.get_custom_form()
if self.object and self.object.context:
context["plan_form"] = self.plan_form
if self.object.context.control_plane.storage_plan_price_per_gib:
context["storage_plan"] = {
"price_per_gib": self.object.context.control_plane.storage_plan_price_per_gib,
}
return context
def _deep_merge(self, base, update):
@ -533,7 +604,17 @@ class ServiceInstanceUpdateView(
current_spec = dict(self.object.spec) if self.object.spec else {}
spec_data = self._deep_merge(current_spec, spec_data)
self.object.update_spec(spec_data=spec_data, updated_by=self.request.user)
compute_plan_assignment = None
if self.plan_form.is_valid():
compute_plan_assignment = self.plan_form.cleaned_data.get(
"compute_plan_assignment"
)
self.object.update_spec(
spec_data=spec_data,
updated_by=self.request.user,
compute_plan_assignment=compute_plan_assignment,
)
messages.success(
self.request,
_("Service instance '{name}' updated successfully.").format(
@ -546,7 +627,7 @@ class ServiceInstanceUpdateView(
return self.form_invalid(form)
except Exception as e:
error_message = self.organization.add_support_message(
_(f"Error updating instance: {str(e)}.")
_("Error updating instance: {error}.").format(error=str(e))
)
form.add_error(None, error_message)
return self.form_invalid(form)