diff --git a/core/apps/companies/serializers/folders/company_folders.py b/core/apps/companies/serializers/folders/company_folders.py index 1b13f5e..3a61991 100644 --- a/core/apps/companies/serializers/folders/company_folders.py +++ b/core/apps/companies/serializers/folders/company_folders.py @@ -1,6 +1,7 @@ from rest_framework import serializers # type: ignore from core.apps.companies.models import CompanyFolderModel +from core.utils.misc import get_context_field class BaseCompanyFolderSerializer(serializers.ModelSerializer): @@ -38,7 +39,7 @@ class CreateCompanyFolderSerializer(BaseCompanyFolderSerializer): class Meta(BaseCompanyFolderSerializer.Meta): ... -class CreateCompanyFolderFromCompanySerializer(CreateCompanyFolderSerializer): +class CreateFolderForCompanySerializer(CreateCompanyFolderSerializer): class Meta(CreateCompanyFolderSerializer.Meta): read_only_fields = ( *CreateCompanyFolderSerializer.Meta.read_only_fields, @@ -46,6 +47,6 @@ class CreateCompanyFolderFromCompanySerializer(CreateCompanyFolderSerializer): ) def create(self, validated_data: dict[str, object]) -> Meta.model: - validated_data["company_id"] = self.context["company_id"] - return super().create(validated_data) # type: ignore + context_data = get_context_field("company", self.context) + return super().create(validated_data | context_data) # type: ignore \ No newline at end of file diff --git a/core/apps/companies/views/companies.py b/core/apps/companies/views/companies.py index ed25389..f890a15 100644 --- a/core/apps/companies/views/companies.py +++ b/core/apps/companies/views/companies.py @@ -34,7 +34,7 @@ from core.apps.companies.serializers import ( CreateCompanyFolderSerializer, BaseCompanyAccountSerializer, - CreateCompanyFolderFromCompanySerializer + CreateFolderForCompanySerializer ) from core.apps.contracts.serializers import ( @@ -50,6 +50,9 @@ from core.apps.contracts.models import ( UserModel = get_user_model() +###################################################################### +# View Only For Company CRUD That Belongs To Admin. +###################################################################### @extend_schema(tags=["Company"]) class CompanyView(BaseViewSetMixin, ModelViewSet): queryset = CompanyModel.objects.all() @@ -72,6 +75,9 @@ class CompanyView(BaseViewSetMixin, ModelViewSet): } +###################################################################### +# company//contract Views +###################################################################### class CompanyContractViewSet(BaseViewSetMixin, GenericViewSet): queryset = CompanyModel.objects.all() permission_classes = [AllowAny] @@ -110,6 +116,9 @@ class CompanyContractViewSet(BaseViewSetMixin, GenericViewSet): return Response(serializer.data, status=status.HTTP_200_OK) +###################################################################### +# company//accounts Views +###################################################################### class CompanyAccountViewSet(BaseViewSetMixin, GenericViewSet): queryset = CompanyModel.objects.all() serializer_class = BaseCompanyAccountSerializer @@ -130,7 +139,6 @@ class CompanyAccountViewSet(BaseViewSetMixin, GenericViewSet): def list_account( self, request: HttpRequest, - pk: uuid.UUID, *args: object, **kwargs: object, ) -> Response: @@ -140,18 +148,20 @@ class CompanyAccountViewSet(BaseViewSetMixin, GenericViewSet): return Response(data=ser.data, status=status.HTTP_200_OK) +###################################################################### +# company//folders Views +###################################################################### class CompanyFolderViewSet(BaseViewSetMixin, GenericViewSet): queryset = CompanyModel.objects.all() permission_classes = [AllowAny] - # serializer_class = BaseCompanyFolderSerializer action_permission_classes = { "list_folder": [IsCompanyAccount], "create_folder": [IsCompanyAccount], } action_serializer_class = { # type: ignore - "list_folder": RetrieveCompanyFolderSerializer, - "create_folder": CreateCompanyFolderFromCompanySerializer, + "list_folder": RetrieveCompanyFolderSerializer, + "create_folder": CreateFolderForCompanySerializer, } @extend_schema( @@ -159,15 +169,12 @@ class CompanyFolderViewSet(BaseViewSetMixin, GenericViewSet): description="List Company Folders" ) @action(methods=["GET"], detail=True, url_path="folders") - def list_folder( - self, - request: HttpRequest, - *args: object, - **kwargs: object, - ) -> Response: + def list_folder(self, *args: object, **kwargs: object) -> Response: company = self.get_object() folders = CompanyFolderModel.objects.filter(company=company) - ser = self.get_serializer(instance=folders, many=True, context={"company_id": company.pk}) # type: ignore + ser = self.get_serializer( + instance=folders, many=True, context={"company_id": company.pk} + ) # type: ignore return Response(data=ser.data, status=status.HTTP_200_OK) @extend_schema( @@ -175,15 +182,9 @@ class CompanyFolderViewSet(BaseViewSetMixin, GenericViewSet): description="Create Folder for company", ) @action(url_path="folders", detail=True, methods=["POST"]) - def create_folder( - self, - request: HttpRequest, - pk: uuid.UUID, - *args: object, - **kwargs: object, - ) -> Response: + def create_folder(self, request: HttpRequest, *args: object, **kwargs: object) -> Response: company = self.get_object() data = request.data.copy() | dict(company=company.id) # type: ignore - ser = CreateCompanyFolderSerializer(data=data) # type: ignore + ser = self.get_serializer(data=data) # type: ignore ser.is_valid(raise_exception=True) return Response(data=ser.data, status=status.HTTP_201_CREATED) diff --git a/core/apps/contracts/views/owners.py b/core/apps/contracts/views/owners.py index a5ba640..c792dc1 100644 --- a/core/apps/contracts/views/owners.py +++ b/core/apps/contracts/views/owners.py @@ -17,7 +17,7 @@ from rest_framework.generics import get_object_or_404 # type: ignore from core.apps.contracts.models import ( ContractOwnerModel, - ContractAttachedFileModel + ContractAttachedFileModel, ) from core.apps.contracts.serializers import ( CreateContractOwnerSerializer, diff --git a/core/utils/misc.py b/core/utils/misc.py new file mode 100644 index 0000000..8b86bd2 --- /dev/null +++ b/core/utils/misc.py @@ -0,0 +1,65 @@ +from rest_framework.exceptions import ValidationError # type: ignore # noqa +from django.utils.translation import gettext as _ + + +def get_context_field(field_name: str, data: dict[str, object]) -> dict[str, object]: + """ + Extracts a specified field or its ID variant from a DRF context-like data dictionary. + + This utility function is intended to simplify extraction of contextual values (typically + passed through DRF serializer `context`) and enforce that at least one of the required + keys is present: + - `` + - `_id` + + The function returns a dictionary containing whichever key is found. If neither key is + present in the given data, a `ValidationError` is raised with an informative message. + + This is especially useful in DRF serializers or views where context-dependent values + are passed and must be validated before use. + + Parameters: + ---------- + field_name : str + The base name of the expected field (e.g., "user", "contract", etc.). + data : dict[str, object] + A dictionary representing the context or other data source to extract from. + Typically, this would be `self.context` from a DRF serializer. + + Returns: + ------- + dict[str, object] + A dictionary containing one of the following key-value pairs: + {field_name: data[field_name]} + or + {field_name + "_id": data[field_name + "_id"]} + + Raises: + ------ + rest_framework.exceptions.ValidationError + If neither `{field_name}` nor `{field_name}_id` is found in the provided data. + + Example: + ------- + >>> get_context_field("user", {"user": user_instance}) + {'user': } + + >>> get_context_field("organization", {"organization_id": 42}) + {'organization_id': 42} + + >>> get_context_field("group", {}) + ValidationError: Missing required context key: either 'group' or 'group_id' + """ + + if field_name in data: + return {field_name: data[field_name]} + + field_id = f"{field_name}_id" + if field_id in data: + return {field_id: data[field_id]} + + raise ValidationError( + _( + "Missing required context key: either '{field_name}' or '{field_id}'" + ).format(field_name=field_name, field_id=field_id) + )