HamidX/demo.courses
0
1"""2Reusable same-organization validation helpers.3 4Centralizes the cross-tenant integrity checks that S3 domain models share, so the5"both sides of a relation must belong to the same organization" rule is written6once and enforced in both `Model.clean()` and serializer `validate()`.7 8These raise Django's ValidationError; DRF serializers translate it to a 400.9Organization values are NEVER taken from client input — they are assigned10server-side from the selected membership — so these checks guard related FKs11(teacher, student, classroom, membership) against pointing at another tenant.12"""13 14from django.core.exceptions import ValidationError15 16 17def validate_same_organization(organization, related_obj, field_name: str) -> None:18 """Ensure `related_obj.organization` matches `organization`.19 20 No-op when either side is unset (other validation handles required-ness).21 """22 if organization is None or related_obj is None:23 return24 related_org_id = getattr(related_obj, "organization_id", None)25 expected_id = getattr(organization, "id", organization)26 if related_org_id is not None and related_org_id != expected_id:27 raise ValidationError(28 {field_name: f"{field_name} must belong to the same organization."}29 )30 31 32def validate_teacher_membership(teacher) -> None:33 """Validate a Teacher's optional membership link.34 35 When `membership` is set it must: belong to the teacher's organization, carry36 the TEACHER role, and be active. Imported lazily to avoid app-loading cycles.37 """38 membership = getattr(teacher, "membership", None)39 if membership is None:40 return41 42 from apps.organizations.models import OrganizationRole43 44 errors = {}45 if teacher.organization_id and membership.organization_id != teacher.organization_id:46 errors["membership"] = "membership must belong to the same organization."47 elif membership.role != OrganizationRole.TEACHER:48 errors["membership"] = "membership role must be 'teacher'."49 elif not membership.is_active:50 errors["membership"] = "membership must be active."51 if errors:52 raise ValidationError(errors)53 