HH/apps/core/form_mixins.py
2026-03-09 16:10:24 +03:00

138 lines
4.9 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 visibility based on user role.
- PX Admins: See dropdown with all active hospitals
- Others: Hidden field, auto-set to user's 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(user=request.user)
"""
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', 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 based on user role."""
hospital_field = self.fields['hospital']
if self.user.is_px_admin():
# PX Admin: Show dropdown with all active hospitals
hospital_field.queryset = Hospital.objects.filter(status='active').order_by('name')
hospital_field.required = True
# Update widget attrs instead of replacing widget to preserve choices
hospital_field.widget.attrs.update({
'class': 'w-full px-4 py-2.5 rounded-xl border border-slate-200 focus:border-navy focus:ring-2 focus:ring-navy/20 transition text-sm bg-white'
})
else:
# Regular user: Hide field and set default
hospital_field.widget = forms.HiddenInput()
hospital_field.required = False
# Set initial value to user's hospital
if self.user.hospital:
hospital_field.initial = self.user.hospital
# Limit queryset to just user's hospital (for validation)
hospital_field.queryset = Hospital.objects.filter(id=self.user.hospital.id)
else:
# User has no hospital - empty queryset
hospital_field.queryset = Hospital.objects.none()
def clean_hospital(self):
"""
Ensure non-PX admins can only use their own hospital.
PX Admins can select any hospital.
"""
hospital = self.cleaned_data.get('hospital')
if not self.user:
return hospital
if self.user.is_px_admin():
# PX Admin must select a hospital
if not hospital:
raise forms.ValidationError("Please select a hospital.")
return hospital
else:
# Non-PX admins: Force user's hospital
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 and self.instance.hospital:
hospital = self.instance.hospital
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()