Skip to content

Commit

Permalink
abstract model view and serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
SKairinos committed Nov 5, 2024
1 parent b5242a9 commit d19d1ab
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 49 deletions.
2 changes: 1 addition & 1 deletion codeforlife/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"""

from .base import BaseSerializer
from .model import ModelSerializer
from .model import BaseModelSerializer, ModelSerializer
from .model_list import ModelListSerializer
16 changes: 6 additions & 10 deletions codeforlife/serializers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,22 @@
from django.views import View
from rest_framework.serializers import BaseSerializer as _BaseSerializer

from ..request import Request
from ..request import BaseRequest

# pylint: disable-next=duplicate-code
if t.TYPE_CHECKING:
from ..user.models import User

RequestUser = t.TypeVar("RequestUser", bound=User)
else:
RequestUser = t.TypeVar("RequestUser")
# pylint: disable=duplicate-code
AnyBaseRequest = t.TypeVar("AnyBaseRequest", bound=BaseRequest)
# pylint: enable=duplicate-code


# pylint: disable-next=abstract-method
class BaseSerializer(_BaseSerializer, t.Generic[RequestUser]):
class BaseSerializer(_BaseSerializer, t.Generic[AnyBaseRequest]):
"""Base serializer to be inherited by all other serializers."""

@property
def request(self):
"""The HTTP request that triggered the view."""

return t.cast(Request[RequestUser], self.context["request"])
return t.cast(AnyBaseRequest, self.context["request"])

@property
def view(self):
Expand Down
27 changes: 16 additions & 11 deletions codeforlife/serializers/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,33 @@
from django.db.models import Model
from rest_framework.serializers import ModelSerializer as _ModelSerializer

from ..request import BaseRequest, Request
from ..types import DataDict
from .base import BaseSerializer

# pylint: disable=duplicate-code
if t.TYPE_CHECKING:
from ..user.models import User
from ..views import BaseModelViewSet, ModelViewSet

RequestUser = t.TypeVar("RequestUser", bound=User)
else:
RequestUser = t.TypeVar("RequestUser")

AnyModel = t.TypeVar("AnyModel", bound=Model)
AnyBaseRequest = t.TypeVar("AnyBaseRequest", bound=BaseRequest)
# pylint: enable=duplicate-code


class ModelSerializer(
BaseSerializer[RequestUser],
class BaseModelSerializer(
BaseSerializer[AnyBaseRequest],
_ModelSerializer[AnyModel],
t.Generic[RequestUser, AnyModel],
t.Generic[AnyBaseRequest, AnyModel],
):
"""Base model serializer for all model serializers."""

instance: t.Optional[AnyModel]

@property
def view(self):
# NOTE: import outside top-level to avoid circular imports.
# pylint: disable-next=import-outside-toplevel
from ..views import ModelViewSet

return t.cast(ModelViewSet[RequestUser, AnyModel], super().view)
view: "BaseModelViewSet[AnyBaseRequest, AnyModel]"

@property
def non_none_instance(self):
Expand All @@ -61,3 +57,12 @@ def validate(self, attrs: DataDict):
# pylint: disable-next=useless-parent-delegation
def to_representation(self, instance: AnyModel) -> DataDict:
return super().to_representation(instance)


class ModelSerializer(
BaseModelSerializer[Request[RequestUser], AnyModel],
t.Generic[RequestUser, AnyModel],
):
"""Base model serializer for all model serializers."""

view: "ModelViewSet[RequestUser, AnyModel]" # type: ignore[assignment]
4 changes: 2 additions & 2 deletions codeforlife/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Created on 24/01/2024 at 13:07:38(+00:00).
"""

from .api import APIView
from .api import APIView, BaseAPIView
from .common import CsrfCookieView, LogoutView
from .decorators import action, cron_job
from .model import ModelViewSet
from .model import BaseModelViewSet, ModelViewSet
15 changes: 11 additions & 4 deletions codeforlife/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@

from rest_framework.views import APIView as _APIView

from ..request import Request
from ..request import BaseRequest, Request

# pylint: disable-next=duplicate-code
# pylint: disable=duplicate-code
if t.TYPE_CHECKING:
from ..user.models import User

RequestUser = t.TypeVar("RequestUser", bound=User)
else:
RequestUser = t.TypeVar("RequestUser")

AnyBaseRequest = t.TypeVar("AnyBaseRequest", bound=BaseRequest)

# pylint: enable=duplicate-code


# pylint: disable-next=missing-class-docstring
class APIView(_APIView, t.Generic[RequestUser]):
request: Request[RequestUser]
class BaseAPIView(_APIView, t.Generic[AnyBaseRequest]):
request: AnyBaseRequest


# pylint: disable-next=missing-class-docstring
class APIView(BaseAPIView[Request[RequestUser]], t.Generic[RequestUser]):
@classmethod
def get_request_user_class(cls) -> t.Type[RequestUser]:
"""Get the request's user class.
Expand Down
56 changes: 35 additions & 21 deletions codeforlife/views/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
from rest_framework.viewsets import ModelViewSet as DrfModelViewSet

from ..permissions import Permission
from ..request import Request
from ..request import BaseRequest, Request
from ..types import KwArgs
from .api import APIView
from .api import APIView, BaseAPIView
from .decorators import action

AnyModel = t.TypeVar("AnyModel", bound=Model)

# pylint: disable-next=duplicate-code
# pylint: disable=duplicate-code
if t.TYPE_CHECKING: # pragma: no cover
from ..serializers import ModelListSerializer, ModelSerializer
from ..serializers import (
BaseModelSerializer,
ModelListSerializer,
ModelSerializer,
)
from ..user.models import User

RequestUser = t.TypeVar("RequestUser", bound=User)
Expand All @@ -41,16 +45,21 @@ class _ModelViewSet(DrfModelViewSet, t.Generic[AnyModel]):
pass


AnyBaseRequest = t.TypeVar("AnyBaseRequest", bound=BaseRequest)

# pylint: enable=duplicate-code


# pylint: disable-next=too-many-ancestors
class ModelViewSet(
APIView[RequestUser],
class BaseModelViewSet(
BaseAPIView[AnyBaseRequest],
_ModelViewSet[AnyModel],
t.Generic[RequestUser, AnyModel],
t.Generic[AnyBaseRequest, AnyModel],
):
"""Base model view set for all model view sets."""

serializer_class: t.Optional[
t.Type["ModelSerializer[RequestUser, AnyModel]"]
t.Type["BaseModelSerializer[AnyBaseRequest, AnyModel]"]
]

@classmethod
Expand Down Expand Up @@ -114,47 +123,52 @@ class _ModelListSerializer(

return serializer

# --------------------------------------------------------------------------
# View Set Actions
# --------------------------------------------------------------------------

# pylint: disable=useless-parent-delegation

def destroy( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().destroy(request, *args, **kwargs)

def create( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().create(request, *args, **kwargs)

def list( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().list(request, *args, **kwargs)

def retrieve( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().retrieve(request, *args, **kwargs)

def update( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().update(request, *args, **kwargs)

def partial_update( # type: ignore[override] # pragma: no cover
self, request: Request[RequestUser], *args, **kwargs
self, request: AnyBaseRequest, *args, **kwargs
):
return super().partial_update(request, *args, **kwargs)

# pylint: enable=useless-parent-delegation

# --------------------------------------------------------------------------
# Bulk Actions
# --------------------------------------------------------------------------

# pylint: disable-next=too-many-ancestors
class ModelViewSet(
BaseModelViewSet[Request[RequestUser], AnyModel],
APIView[RequestUser],
t.Generic[RequestUser, AnyModel],
):
"""Base model view set for all model view sets."""

serializer_class: t.Optional[
t.Type["ModelSerializer[RequestUser, AnyModel]"]
]

def get_bulk_queryset(self, lookup_values: t.Collection):
"""Get the queryset for a bulk action.
Expand Down

0 comments on commit d19d1ab

Please sign in to comment.