diff --git a/.gitignore b/.gitignore index 894a44c..d256c7a 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,7 @@ venv.bak/ # mypy .mypy_cache/ + + +#ide +.idea \ No newline at end of file diff --git a/READEME.md b/READEME.md new file mode 100644 index 0000000..96a73ea --- /dev/null +++ b/READEME.md @@ -0,0 +1,174 @@ +#Django Rest Framework Mango + +A set of viewset mixin for the [Django REST Framework.](https://www.django-rest-framework.org/) + +##Installation +`pip install djangorestframework-mango` + +##Usage + +###ActionMixin +It has six action methods that can be use instead of compare action with str +- `is_create_action()` +- `is_retrieve_action()` +- `is_list_action()` +- `is_update_action()` +- `is_partial_update_action()` +- `is_destroy_action()` + + +```python +class ViewSet(ActionMixin, viewsets.GenericViewSet): + queryset = Model.objects.all() + serializer_class = ModelSerializer + + + def get_queryset(self): + queryset = super().get_queryset() + + if self.is_create_action: + # change queryset for create + queryset = queryset.change_for_create() + elif self.is_retrieve_action(): + # change queryset for retrieve + queryset = queryset.change_for_retrieve() + elif self.is_list_action(): + # change queryset for list + queryset = queryset.change_for_list() + elif self.is_update_action(): + # change queryset for update + queryset = queryset.change_for_update() + elif self.is_partial_update_action(): + # change queryset for partial update + queryset = queryset.change_for_partial_update() + elif self.is_destroy_action(): + # change queryset for destroy + queryset = queryset.change_for_destroy() + + return queryset +``` + +###QuerysetMixin +It find action base queryset method and run it + +```python +class ViewSet(QuerysetMixin, viewsets.GenericViewSet): + queryset = Model.objects.all() + serializer_class = ModelSerializer + + # this method run automatically when this viewset gets create action + def create_queryset(self, queryset): + queryset = queryset.change_for_create() + return queryset + + # this method run automatically when this viewset gets list action + def list_queryset(self, queryset): + queryset = queryset.change_for_list() + return queryset + + # this method run automatically when this viewset gets retrieve action + def retrieve_queryset(self, queryset): + queryset = queryset.change_for_retrieve() + return queryset + # this method run automatically when this viewset gets update action + def update_queryset(self, queryset): + queryset = queryset.change_for_update() + return queryset + # this method run automatically when this viewset gets partial update action + def partil_update_queryset(self, queryset): + queryset = queryset.change_for_partial_update() + return queryset + # this method run automatically when this viewset gets destroy action + def destroy_queryset(self, queryset): + queryset = queryset.change_for_delete() + return queryset + + # this method run automatically when this viewset gets update_extra_profile action + def update_extra_profile_queryset(self, queryset): + queryset = queryset.change_for_update_extra_profile() + return queryset + + @action(methods['POST'], detail=True) + def update_extra_profile(self, request, pk=None): + # this method calls update_extra_profile_queryset() internally + queryset = self.get_queryset() + + return Response(serializer.data) +``` + +###SerializerMixin +You can define multi serializers by action + +```python +class ViewSet(QuerysetMixin, viewsets.GenericViewSet): + queryset = Model.objects.all() + serializer_class = ModelSerializer + serializer_class_by_actions = { + 'create': ModelCreateSerializer, + 'list': ModelListSerializer, + 'retrieve': ModelRetrieveSerializer, + 'update': ModelUpdateSerializer, + 'partial_update': ModelParitlaUpdateSerializer, + 'destory': ModelDestorySerializer, + 'update_extra_profile': ModelUpdateExtraProfileSerializer, + } + + @action(methods['POST'], detail=True) + def update_extra_profile(self, request, pk=None): + # self.get_serializer returns ModelUpdateExtraProfileSerializer + serializer = self.get_serializer() + + return Response(serializer.data) +``` + +###PermissionMixin +You can define multi permissions by action + +```python +class ViewSet(QuerysetMixin, viewsets.GenericViewSet): + queryset = Model.objects.all() + serializer_class = ModelSerializer + permission_by_actions = { + 'create': [Authenticated], + 'list': [ReadOnly], + 'retrieve': [AllowAny], + 'update': [Owner], + 'partial_update': [Owner], + 'destory': [Owner], + 'update_extra_profile': [Owner], + } + + @action(methods['POST'], detail=True) + def update_extra_profile(self, request, pk=None): + # this method requires Owner permission + serializer = self.get_serializer() + + return Response(serializer.data) +``` + +###SessionMiddleware +You can use session data within request life cycle. +- add SessionMiddleware +- use session from view, serializer and model + +```python +class ViewSet(viewsets.GenericViewSet): + queryset = Model.objects.all() + serializer_class = ModelSerializer + + def list_queryset(self, queryset): + session = SessionMiddleware.get_session() + session['current_user'] = self.request.user + + return queryset + +class Model(DjangoModel): + + @property + def current_user(self): + session = SessionMiddleware.get_session() + session['current_user'] = self.request.user + + return session['current_user'] + +``` \ No newline at end of file diff --git a/django-rest-framework-mango/__init__.py b/django-rest-framework-mango/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django-rest-framework-mango/middlewares.py b/django-rest-framework-mango/middlewares.py new file mode 100644 index 0000000..142c3e7 --- /dev/null +++ b/django-rest-framework-mango/middlewares.py @@ -0,0 +1,27 @@ +import threading + + +class SessionMiddleware: + _sessions = {} + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + self._init_session(request) + + response = self.get_response(request) + + self._remove_session() + + return response + + def _init_session(self, request): + self._sessions[threading.current_thread()] = request + + def _remove_session(self): + self._sessions.pop(threading.current_thread(), None) + + @classmethod + def get_session(cls): + return cls._sessions.get(threading.current_thread()) diff --git a/django-rest-framework-mango/mixins.py b/django-rest-framework-mango/mixins.py new file mode 100644 index 0000000..7975f25 --- /dev/null +++ b/django-rest-framework-mango/mixins.py @@ -0,0 +1,63 @@ +class ActionMixin: + def is_create_action(self): + return 'create' == self.action + + def is_retrieve_action(self): + return 'retrieve' == self.action + + def is_list_action(self): + return 'list' == self.action + + def is_update_action(self): + return 'update' == self.action + + def is_partial_update_action(self): + return 'partial_update' == self.action + + def is_destroy_action(self): + return 'destroy' == self.action + + +class QuerysetMixin(ActionMixin): + def get_queryset(self): + queryset = super().get_queryset() + + if self.is_create_action and hasattr(self, 'create_queryset'): + queryset = self.create_queryset(queryset) + elif self.is_retrieve_action() and hasattr(self, 'retrieve_queryset'): + queryset = self.retrieve_queryset(queryset) + elif self.is_list_action() and hasattr(self, 'list_queryset'): + queryset = self.list_queryset(queryset) + elif self.is_update_action() and hasattr(self, 'update_queryset'): + queryset = self.update_queryset(queryset) + elif self.is_partial_update_action() and hasattr(self, 'partial_update_queryset'): + queryset = self.partial_update_queryset(queryset) + elif self.is_destroy_action() and hasattr(self, 'destroy_queryset'): + queryset = self.destroy_queryset(queryset) + elif hasattr(self, f'{self.action}_queryset'): + queryset_method = getattr(self, f'{self.action}_queryset') + queryset = queryset_method(queryset) + + return queryset + + +class SerializerMixin: + def get_serializer_class(self): + if hasattr(self, 'serializer_class_by_actions'): + return self.serializer_class_by_actions.get(self.action, self.serializer_class) + + return self.serializer_class + + +class PermissionMixin: + def get_permissions(self): + permission_classes = self.permission_classes + + if hasattr(self, 'permission_by_actions'): + permission_classes = self.permission_by_actions.get(self.action, self.permission_classes) + + return [permission() for permission in permission_classes] + + +class MangoMixin(QuerysetMixin, SerializerMixin, PermissionMixin): + pass diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c821d6b --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +from setuptools import setup + +setup(name='django-rest-framework-mango', + version='0.1', + url='https://github.com/legshort/django-rest-framework-mango/', + license='MIT', + author='Jun Young Lee', + author_email='legshort@gmail.com', + description='A set of viewset mixin for the Django REST Framework.', + classifiers=[ + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + ], + keywords=['djangorestframework', 'drf', 'util', 'viewset'], + packages=['django-rest-framework-mango'], + long_description=open('README.md').read(), + install_requires=[ + 'django', + 'djangorestframework', ] + )