HH/apps/core/form_mixins.py
2026-03-15 23:48:45 +03:00

134 lines
4.5 KiB
Python

"""
Form mixins for tenant-aware forms.
Provides mixins to handle hospital field visibility based on user role.
"""
from django import forms
from apps.organizations.models import Hospital
class HospitalFieldMixin:
"""
Mixin to handle hospital field - always hidden, auto-set based on user context.
- PX Admins: Hidden field, auto-set from session (request.tenant_hospital)
- Others: Hidden field, auto-set from user's hospital (User.hospital)
Usage:
class MyForm(HospitalFieldMixin, forms.ModelForm):
class Meta:
model = MyModel
fields = ['hospital', ...]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Hospital field is automatically configured
In views:
form = MyForm(request=request)
"""
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request", None)
self.user = self.request.user if self.request else None
super().__init__(*args, **kwargs)
if self.user and "hospital" in self.fields:
self._setup_hospital_field()
def _setup_hospital_field(self):
"""Configure hospital field - always hidden, auto-set based on user context."""
hospital_field = self.fields["hospital"]
hospital_field.widget = forms.HiddenInput()
hospital_field.required = False
hospital = None
if self.user.is_px_admin():
hospital = getattr(self.request, "tenant_hospital", None)
else:
hospital = self.user.hospital
if hospital:
hospital_field.initial = hospital
hospital_field.queryset = Hospital.objects.filter(id=hospital.id)
else:
hospital_field.queryset = Hospital.objects.none()
def clean_hospital(self):
"""
Auto-set hospital based on user context.
"""
hospital = self.cleaned_data.get("hospital")
if not self.user:
return hospital
if self.user.is_px_admin():
hospital = getattr(self.request, "tenant_hospital", None)
if not hospital:
raise forms.ValidationError("No hospital selected. Please select a hospital first.")
return hospital
else:
if self.user.hospital:
return self.user.hospital
else:
raise forms.ValidationError("You do not have a hospital assigned. Please contact your administrator.")
class DepartmentFieldMixin:
"""
Mixin to handle department field filtering based on user's hospital.
- Filters departments to only show those in the selected/current hospital
- Works with HospitalFieldMixin
Usage:
class MyForm(HospitalFieldMixin, DepartmentFieldMixin, forms.ModelForm):
class Meta:
model = MyModel
fields = ['hospital', 'department', ...]
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if "department" in self.fields:
self._setup_department_field()
def _setup_department_field(self):
"""Configure department field with hospital-based filtering."""
from apps.organizations.models import Department
department_field = self.fields["department"]
# Get the hospital (either from form data or user's hospital)
hospital = None
if self.data.get("hospital"):
try:
hospital = Hospital.objects.get(id=self.data["hospital"])
except Hospital.DoesNotExist:
pass
elif self.initial.get("hospital"):
hospital = self.initial["hospital"]
elif self.instance and self.instance.pk:
# Only access hospital if instance is saved (has pk)
try:
hospital = self.instance.hospital
except Exception:
# Hospital not set (RelatedObjectDoesNotExist or similar)
pass
elif self.user and self.user.is_px_admin():
hospital = getattr(self.request, "tenant_hospital", None)
elif self.user and self.user.hospital:
hospital = self.user.hospital
if hospital:
# Filter departments to user's hospital
department_field.queryset = Department.objects.filter(hospital=hospital, status="active").order_by("name")
else:
# No hospital context - empty queryset
department_field.queryset = Department.objects.none()