For your first question, about connecting the UserViewSet, you would use those in your urls file like this:
urlpatterns = [
url(
r'^users/$',
UserViewSet.as_view({'get': 'list'}),
name='user-list',
),
url(
r'^users/(?P<pk>\d+)/$',
UserViewSet.as_view({'get': 'retrieve'}),
name='user-detail',
),
]
This is a read-only implementation of your User model using ViewSets. If you want to list all the users you can request /users/
, and if you want to get the user with an id of 1, you would request /users/1/
.
What you should understand here is that there are actions which act on your Model, such as listing users or creating a new user, and actions which act on an instance of your Model. For example, if you want to retrieve, update, or delete a User instance, you'd need to include the users primary key in the URL so you can fetch that user.
Your TaskViewSet is not read-only, so let's take a look at what that class should look like.
class TaskViewSet(ModelViewSet):
queryset = Task
serializer_class = TaskSerializer
lookup_field = 'pk' # This is the default
lookup_url_kwarg = 'pk' # This is the default
That is a simple, generic ModelViewSet that you could implement in your urls file like this:
urlpatterns = [
url(
r'^tasks/$',
TaskViewSet.as_view({'get': 'list', 'post': 'create'}),
name='task-list',
),
url(
r'^tasks/(?P<pk>\d+)/$',
TaskViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}),
name='task-detail',
),
]
Now you can do anything with your Model, you can list, create, retrieve, update, and delete objects. You'll see there's a bit of a pattern forming here using the ModelViewSet, this is where the convenience of routers stands out. Django Rest Frameworks router implementation essentially works by taking a path and a ViewSet and then constructing the urls.
In this example we are using SimpleRouter to generate our url patterns.
router = SimpleRouter()
router.register('users', UserViewSet) # UserViewSet is ReadOnlyModelViewSet
router.register('tasks', TaskViewSet) # TaskViewSet is ModelViewSet
urlpatterns = [] + router.urls
Which would generate:
urlpatterns = [
url(
r'^users/$',
UserViewSet.as_view({'get': 'list'}),
name='user-list',
),
url(
r'^users/(?P<pk>\d+)/$',
UserViewSet.as_view({'get': 'retrieve'}),
name='user-detail',
),
url(
r'^tasks/$',
TaskViewSet.as_view({'get': 'list', 'post': 'create'}),
name='task-list',
),
url(
r'^tasks/(?P<pk>\d+)/$',
TaskViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}),
name='task-detail',
),
]
I hope this makes sense so far, and you can see how you might use these classes to reduce how much code you'll need to write.
Now I'll explain what the @detail_route
and @list_route
decorators are doing. These decorators help the Router classes register custom methods on your ViewSet.
For rest framework 3.8 and above @detail_route
and @list_route
have been deprecated in favour of the @action
decorator Replace @detail_route(...)
with @action(detail=True, ...)
.
Replace @list_route(...)
uses with @action(detail=False, ...)
.
You should use the @list_route
decorator for actions that make sense to be called upon the Model and not an instance of the model, an example of this might be if you wanted to provide some endpoint to download a report on your model as csv. You should use the @detail_route
decorator for actions that happen for an instance of your Model. I'm going to extend the TaskViewSet example from before.
class TaskViewSet(ModelViewSet):
queryset = Task
serializer_class = TaskSerializer
lookup_field = 'pk' # This is the default
lookup_url_kwarg = 'pk' # This is the default
@list_route(methods=['get'])
def download(self, request, *args, **kwargs):
"""Download queryset as xlsx"""
qs = self.get_queryset()
return queryset_to_excel(qs) # simple example
@detail_route(methods=['get'])
def details(self, request, *args, **kwargs):
"""Return intricate details of Task"""
object = self.get_object()
return object.get_intricate_task_details()
If we use this TaskViewSet with a router:
router = SimpleRouter()
router.register('tasks', TaskViewSet)
urlpatterns = [] + router.urls
I've added a download method on the list, to download the queryset as an Excel file, and I've added a details method to the detail, which will return some extra information that may be expensive to retrieve, so we don't want it on the normal detail response. Then we will get a url configuration like:
urlpatterns = [
url(
r'^tasks/$',
TaskViewSet.as_view({'get': 'list', 'post': 'create'}),
name='task-list',
),
url(
r'^tasks/download/$',
TaskViewSet.as_view({'get': 'download'}),
name='task-download',
),
url(
r'^tasks/(?P<pk>\d+)/$',
TaskViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}),
name='task-detail',
),
url(
r'^tasks/(?P<pk>\d+)/details/$',
TaskViewSet.as_view({'get': 'detail'}),
name='task-details',
),
]
The router has now generated additional routes for the custom methods defined on the TaskViewSet.
I would recommend reading up on rest frameworks documentation on ViewSet and SimpleRouter.
http://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
http://www.django-rest-framework.org/api-guide/routers/#simplerouter