Skip to content
This repository was archived by the owner on Jul 3, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ if `Article` had 2 transitions, `delete` and `publish`, the following API calls

### Custom route arguments

Passing arguments to the `@detail_route` decorator can be done by specifiying
Passing arguments to the `@action` decorator can be done by specifiying
them in the `get_viewset_transition_action_mixin` method:

```python
Expand All @@ -46,7 +46,7 @@ class ArticleViewSet(
queryset = Article.objects.all()
```

This will set `permission_classes` on each `@detail_route` for all transitions.
This will set `permission_classes` on each detail `@action` for all transitions.
There is currrently no way to specify individual arguments for each transition.

### Saving
Expand Down
35 changes: 23 additions & 12 deletions drf_fsm_transitions/viewset_mixins.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,56 @@
from rest_framework.decorators import detail_route
'''Heavily inspired by: https://github.com/jacobh/drf-fsm-transitions

Modified to work with DRF >= 3.8 routing semantics
'''
from django_fsm import can_proceed
from rest_framework import exceptions
from rest_framework.decorators import action
from rest_framework.response import Response


def get_transition_viewset_method(transition_name, **kwargs):
def get_transition_viewset_method(transition_name, url_name=None, **kwargs):
'''
Create a viewset method for the provided `transition_name`
Create a viewset method for the provided `transition_name`. Requires DRF >= 3.8
'''
@detail_route(methods=['post'], **kwargs)
@action(detail=True, methods=['post'], url_name=url_name, url_path=transition_name, **kwargs)
def inner_func(self, request, pk=None, **kwargs):
object = self.get_object()
transition_method = getattr(object, transition_name)
if can_proceed(transition_method, self.request.user):

transition_method(by=self.request.user)
# Perform the requested transition
transition_method(request=self.request, by=self.request.user)

if self.save_after_transition:
object.save()
if self.save_after_transition:
object.save()
else:
raise exceptions.PermissionDenied(
'User {} cannot perform transition {}'.format(self.request.user, transition_name))

serializer = self.get_serializer(object)
return Response(serializer.data)

inner_func.__name__ = transition_name # Needed for DRF >= 3.8, see: router.get_routes
return inner_func


def get_viewset_transition_action_mixin(model, **kwargs):
'''
Find all transitions defined on `model`, then create a corresponding
viewset action method for each and apply it to `Mixin`. Finally, return
`Mixin`
viewset action method for each and apply it to `Mixin`. Return the Mixin.
'''
instance = model()

class Mixin(object):
save_after_transition = True

transitions = instance.get_all_status_transitions()
transitions = instance.get_all_state_transitions()
transition_names = set(x.name for x in transitions)
for transition_name in transition_names:
url_name = model._meta.model_name + '-' + transition_name.replace('_', '-')
setattr(
Mixin,
transition_name,
get_transition_viewset_method(transition_name, **kwargs)
get_transition_viewset_method(transition_name, url_name=url_name, **kwargs)
)

return Mixin