-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] feat(mixin, middleware): add a set of view mixin and session mi…
…ddleware (#1) * feat(mixin, middleware): add a set of view mixin and session middleware * docs(readme): add readme * feat(setup): add setup
- Loading branch information
Showing
6 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,3 +102,7 @@ venv.bak/ | |
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
|
||
#ide | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'] | ||
|
||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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='[email protected]', | ||
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', ] | ||
) |