2014-11-25 19:04:38 +03:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" >
< meta charset = "utf-8" >
2015-05-15 11:19:49 +03:00
< title > Viewsets - Django REST framework< / title >
2014-11-25 19:04:38 +03:00
< link href = "../../img/favicon.ico" rel = "icon" type = "image/x-icon" >
2018-10-11 16:48:33 +03:00
< link rel = "canonical" href = "https://www.django-rest-framework.org/api-guide/viewsets/" / >
2014-11-25 19:04:38 +03:00
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta name = "description" content = "Django, API, REST, Viewsets" >
< meta name = "author" content = "Tom Christie" >
<!-- Le styles -->
< link href = "../../css/prettify.css" rel = "stylesheet" >
< link href = "../../css/bootstrap.css" rel = "stylesheet" >
< link href = "../../css/bootstrap-responsive.css" rel = "stylesheet" >
< link href = "../../css/default.css" rel = "stylesheet" >
<!-- Le HTML5 shim, for IE6 - 8 support of HTML5 elements -->
<!-- [if lt IE 9]>
< script src = "http://html5shim.googlecode.com/svn/trunk/html5.js" > < / script >
<![endif]-->
< script type = "text/javascript" >
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18852272-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
< / script >
< style >
2016-05-26 13:48:39 +03:00
#sidebarInclude img {
margin-bottom: 10px;
}
#sidebarInclude a.promo {
2014-11-25 19:04:38 +03:00
color: black;
2016-05-26 13:48:39 +03:00
}
2014-11-25 19:04:38 +03:00
@media (max-width: 767px) {
div.promo {
display: none;
}
}
< / style >
< / head >
< body onload = "prettyPrint()" class = "-page" >
< div class = "wrapper" >
< div class = "navbar navbar-inverse navbar-fixed-top" >
< div class = "navbar-inner" >
< div class = "container-fluid" >
2017-05-12 19:15:00 +03:00
< a class = "repo-link btn btn-primary btn-small" href = "https://github.com/encode/django-rest-framework/tree/master" > GitHub< / a >
2019-07-15 14:38:39 +03:00
< a class = "repo-link btn btn-inverse btn-small " rel = "next" href = "api-guide/routers/" >
2014-11-25 19:04:38 +03:00
Next < i class = "icon-arrow-right icon-white" > < / i >
< / a >
2019-07-15 14:38:39 +03:00
< a class = "repo-link btn btn-inverse btn-small " rel = "prev" href = "api-guide/generic-views/" >
2014-11-25 19:04:38 +03:00
< i class = "icon-arrow-left icon-white" > < / i > Previous
< / a >
2015-06-04 17:37:22 +03:00
< a id = "search_modal_show" class = "repo-link btn btn-inverse btn-small" href = "#mkdocs_search_modal" data-toggle = "modal" data-target = "#mkdocs_search_modal" > < i class = "icon-search icon-white" > < / i > Search< / a >
2014-11-25 19:04:38 +03:00
< a class = "btn btn-navbar" data-toggle = "collapse" data-target = ".nav-collapse" >
< span class = "icon-bar" > < / span >
< span class = "icon-bar" > < / span >
< span class = "icon-bar" > < / span >
< / a >
2018-10-11 16:48:33 +03:00
< a class = "brand" href = "https://www.django-rest-framework.org/" > Django REST framework< / a >
2014-11-25 19:04:38 +03:00
< div class = "nav-collapse collapse" >
<!-- Main navigation -->
< ul class = "nav navbar-nav" >
2015-06-04 17:37:22 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "" > Home< / a >
2015-06-04 17:37:22 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li class = "dropdown" >
2014-11-25 19:31:00 +03:00
< a href = "#" class = "dropdown-toggle" data-toggle = "dropdown" > Tutorial < b class = "caret" > < / b > < / a >
2014-11-25 19:04:38 +03:00
< ul class = "dropdown-menu" >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/quickstart/" > Quickstart< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/1-serialization/" > 1 - Serialization< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/2-requests-and-responses/" > 2 - Requests and responses< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/3-class-based-views/" > 3 - Class based views< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/4-authentication-and-permissions/" > 4 - Authentication and permissions< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/5-relationships-and-hyperlinked-apis/" > 5 - Relationships and hyperlinked APIs< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "tutorial/6-viewsets-and-routers/" > 6 - Viewsets and routers< / a >
2016-07-14 15:05:57 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< / ul >
< / li >
< li class = "dropdown active" >
< a href = "#" class = "dropdown-toggle" data-toggle = "dropdown" > API Guide < b class = "caret" > < / b > < / a >
< ul class = "dropdown-menu" >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/requests/" > Requests< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/responses/" > Responses< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/views/" > Views< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/generic-views/" > Generic views< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li class = "active" >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/viewsets/" > Viewsets< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/routers/" > Routers< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/parsers/" > Parsers< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/renderers/" > Renderers< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/serializers/" > Serializers< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/fields/" > Serializer fields< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/relations/" > Serializer relations< / a >
2014-11-25 19:04:38 +03:00
< / li >
2014-12-01 15:20:07 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/validators/" > Validators< / a >
2014-12-01 15:20:07 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/authentication/" > Authentication< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/permissions/" > Permissions< / a >
2014-11-25 19:04:38 +03:00
< / li >
2018-10-11 16:48:33 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/caching/" > Caching< / a >
2018-10-11 16:48:33 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/throttling/" > Throttling< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/filtering/" > Filtering< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/pagination/" > Pagination< / a >
2014-11-25 19:04:38 +03:00
< / li >
2015-03-06 15:05:16 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/versioning/" > Versioning< / a >
2015-03-06 15:05:16 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/content-negotiation/" > Content negotiation< / a >
2014-11-25 19:04:38 +03:00
< / li >
2014-12-18 18:42:42 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/metadata/" > Metadata< / a >
2014-12-18 18:42:42 +03:00
< / li >
2016-07-07 11:41:32 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/schemas/" > Schemas< / a >
2016-07-07 11:41:32 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/format-suffixes/" > Format suffixes< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/reverse/" > Returning URLs< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/exceptions/" > Exceptions< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/status-codes/" > Status codes< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/testing/" > Testing< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "api-guide/settings/" > Settings< / a >
2014-11-25 19:04:38 +03:00
< / li >
< / ul >
< / li >
< li class = "dropdown" >
< a href = "#" class = "dropdown-toggle" data-toggle = "dropdown" > Topics < b class = "caret" > < / b > < / a >
< ul class = "dropdown-menu" >
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/documenting-your-api/" > Documenting your API< / a >
2014-11-25 19:04:38 +03:00
< / li >
2016-07-07 11:41:32 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/api-clients/" > API Clients< / a >
2016-07-07 11:41:32 +03:00
< / li >
2015-03-06 15:05:16 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/internationalization/" > Internationalization< / a >
2015-03-06 15:05:16 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/ajax-csrf-cors/" > AJAX, CSRF & CORS< / a >
2014-11-25 19:04:38 +03:00
< / li >
2015-10-28 14:35:39 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/html-and-forms/" > HTML & Forms< / a >
2015-10-28 14:35:39 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/browser-enhancements/" > Browser Enhancements< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/browsable-api/" > The Browsable API< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "topics/rest-hypermedia-hateoas/" > REST, Hypermedia & HATEOAS< / a >
2014-11-25 19:04:38 +03:00
< / li >
2018-10-11 16:48:33 +03:00
< / ul >
< / li >
< li class = "dropdown" >
< a href = "#" class = "dropdown-toggle" data-toggle = "dropdown" > Community < b class = "caret" > < / b > < / a >
< ul class = "dropdown-menu" >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/tutorials-and-resources/" > Tutorials and Resources< / a >
< / li >
< li >
< a href = "community/third-party-packages/" > Third Party Packages< / a >
2017-02-25 22:59:44 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/contributing/" > Contributing to REST framework< / a >
2014-11-25 19:04:38 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/project-management/" > Project management< / a >
2014-11-25 19:04:38 +03:00
< / li >
2014-12-18 16:49:50 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/release-notes/" > Release Notes< / a >
2014-12-18 16:49:50 +03:00
< / li >
2017-03-09 17:59:14 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.10-announcement/" > 3.10 Announcement< / a >
2017-03-09 17:59:14 +03:00
< / li >
2018-10-18 13:50:44 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.9-announcement/" > 3.9 Announcement< / a >
2018-10-18 13:50:44 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.8-announcement/" > 3.8 Announcement< / a >
2014-11-25 19:04:38 +03:00
< / li >
2014-12-01 15:20:07 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.7-announcement/" > 3.7 Announcement< / a >
2014-12-01 15:20:07 +03:00
< / li >
2015-08-06 16:31:52 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.6-announcement/" > 3.6 Announcement< / a >
2015-08-06 16:31:52 +03:00
< / li >
2015-10-28 14:35:39 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.5-announcement/" > 3.5 Announcement< / a >
2015-10-28 14:35:39 +03:00
< / li >
2016-07-14 15:05:57 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.4-announcement/" > 3.4 Announcement< / a >
2016-07-14 15:05:57 +03:00
< / li >
2016-10-20 18:31:37 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.3-announcement/" > 3.3 Announcement< / a >
2016-10-20 18:31:37 +03:00
< / li >
2017-03-09 17:59:14 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.2-announcement/" > 3.2 Announcement< / a >
2017-03-09 17:59:14 +03:00
< / li >
2017-10-06 15:06:25 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.1-announcement/" > 3.1 Announcement< / a >
2017-10-06 15:06:25 +03:00
< / li >
2018-04-03 16:54:40 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/3.0-announcement/" > 3.0 Announcement< / a >
2018-04-03 16:54:40 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/kickstarter-announcement/" > Kickstarter Announcement< / a >
2014-11-25 19:04:38 +03:00
< / li >
2016-05-26 13:48:39 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/mozilla-grant/" > Mozilla Grant< / a >
2016-05-26 13:48:39 +03:00
< / li >
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/funding/" > Funding< / a >
2016-05-26 13:48:39 +03:00
< / li >
2014-11-25 19:04:38 +03:00
< li >
2019-07-15 14:38:39 +03:00
< a href = "community/jobs/" > Jobs< / a >
2014-11-25 19:04:38 +03:00
< / li >
< / ul >
< / li >
< / ul >
< / div >
<!-- /.nav - collapse -->
< / div >
< / div >
< / div >
< div class = "body-content" >
< div class = "container-fluid" >
<!-- Search Modal -->
2015-06-04 17:37:22 +03:00
< div id = "mkdocs_search_modal" class = "modal hide fade" tabindex = "-1" role = "dialog" aria-labelledby = "myModalLabel" aria-hidden = "true" >
2014-11-25 19:04:38 +03:00
< div class = "modal-header" >
< button type = "button" class = "close" data-dismiss = "modal" aria-hidden = "true" > × < / button >
< h3 id = "myModalLabel" > Documentation search< / h3 >
< / div >
< div class = "modal-body" >
2015-08-06 16:31:52 +03:00
< form role = "form" autocomplete = "off" >
2015-06-04 17:37:22 +03:00
< div class = "form-group" >
< input type = "text" name = "q" class = "form-control" placeholder = "Search..." id = "mkdocs-search-query" >
< / div >
< / form >
< div id = "mkdocs-search-results" > < / div >
2014-11-25 19:04:38 +03:00
< / div >
< div class = "modal-footer" >
< button class = "btn" data-dismiss = "modal" aria-hidden = "true" > Close< / button >
< / div >
< / div >
< div class = "row-fluid" >
< div class = "span3" >
< div id = "table-of-contents" >
< ul class = "nav nav-list side-nav well sidebar-nav-fixed" >
< li class = "main" >
< a href = "#viewsets" > ViewSets< / a >
< / li >
< li >
< a href = "#example" > Example< / a >
< / li >
2017-12-20 17:34:31 +03:00
< li >
< a href = "#viewset-actions" > ViewSet actions< / a >
< / li >
2018-04-03 16:37:24 +03:00
< li >
< a href = "#introspecting-viewset-actions" > Introspecting ViewSet actions< / a >
< / li >
2014-11-25 19:04:38 +03:00
< li >
< a href = "#marking-extra-actions-for-routing" > Marking extra actions for routing< / a >
< / li >
2017-12-20 17:34:31 +03:00
< li >
< a href = "#reversing-action-urls" > Reversing action URLs< / a >
< / li >
2014-11-25 19:04:38 +03:00
< li class = "main" >
< a href = "#api-reference" > API Reference< / a >
< / li >
< li >
< a href = "#viewset" > ViewSet< / a >
< / li >
< li >
< a href = "#genericviewset" > GenericViewSet< / a >
< / li >
< li >
< a href = "#modelviewset" > ModelViewSet< / a >
< / li >
< li >
< a href = "#readonlymodelviewset" > ReadOnlyModelViewSet< / a >
< / li >
< li class = "main" >
< a href = "#custom-viewset-base-classes" > Custom ViewSet base classes< / a >
< / li >
< li >
< a href = "#example_3" > Example< / a >
< / li >
2016-05-26 13:48:39 +03:00
< div class = "promo" >
< hr / >
< div id = "sidebarInclude" >
< / div >
2014-11-25 19:04:38 +03:00
< / ul >
< / div >
< / div >
< div id = "main-content" class = "span9" >
2015-06-04 17:37:22 +03:00
2018-10-16 15:02:48 +03:00
< a class = "github" href = "https://github.com/encode/django-rest-framework/tree/master/rest_framework/viewsets.py" >
< span class = "label label-info" > viewsets.py< / span >
2015-06-04 17:37:22 +03:00
< / a >
2014-11-25 19:04:38 +03:00
2015-11-04 17:59:23 +03:00
< h1 id = "viewsets" > < a class = "toclink" href = "#viewsets" > ViewSets< / a > < / h1 >
2014-11-25 19:04:38 +03:00
< blockquote >
< p > After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output.< / p >
2018-10-11 16:48:33 +03:00
< p > — < a href = "https://guides.rubyonrails.org/routing.html" > Ruby on Rails Documentation< / a > < / p >
2014-11-25 19:04:38 +03:00
< / blockquote >
< p > Django REST framework allows you to combine the logic for a set of related views in a single class, called a < code > ViewSet< / code > . In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.< / p >
< p > A < code > ViewSet< / code > class is simply < strong > a type of class-based View, that does not provide any method handlers< / strong > such as < code > .get()< / code > or < code > .post()< / code > , and instead provides actions such as < code > .list()< / code > and < code > .create()< / code > .< / p >
< p > The method handlers for a < code > ViewSet< / code > are only bound to the corresponding actions at the point of finalizing the view, using the < code > .as_view()< / code > method.< / p >
< p > Typically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.< / p >
2015-11-04 17:59:23 +03:00
< h2 id = "example" > < a class = "toclink" href = "#example" > Example< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > Let's define a simple viewset that can be used to list or retrieve all the users in the system.< / p >
< pre > < code > from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
"""
2015-06-23 14:05:53 +03:00
A simple ViewSet for listing or retrieving users.
2014-11-25 19:04:38 +03:00
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
< / code > < / pre >
< p > If we need to, we can bind this viewset into two separate views, like so:< / p >
< pre > < code > user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
< / code > < / pre >
< p > Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.< / p >
< pre > < code > from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
2018-10-11 16:48:33 +03:00
router.register(r'users', UserViewSet, basename='user')
2014-11-25 19:04:38 +03:00
urlpatterns = router.urls
< / code > < / pre >
< p > Rather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:< / p >
< pre > < code > class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
< / code > < / pre >
< p > There are two main advantages of using a < code > ViewSet< / code > class over using a < code > View< / code > class.< / p >
< ul >
< li > Repeated logic can be combined into a single class. In the above example, we only need to specify the < code > queryset< / code > once, and it'll be used across multiple views.< / li >
< li > By using routers, we no longer need to deal with wiring up the URL conf ourselves.< / li >
< / ul >
< p > Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.< / p >
2017-12-20 17:34:31 +03:00
< h2 id = "viewset-actions" > < a class = "toclink" href = "#viewset-actions" > ViewSet actions< / a > < / h2 >
< p > The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below:< / p >
2014-11-25 19:04:38 +03:00
< pre > < code > class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
< / code > < / pre >
2018-04-03 16:37:24 +03:00
< h2 id = "introspecting-viewset-actions" > < a class = "toclink" href = "#introspecting-viewset-actions" > Introspecting ViewSet actions< / a > < / h2 >
< p > During dispatch, the following attributes are available on the < code > ViewSet< / code > .< / p >
< ul >
< li > < code > basename< / code > - the base to use for the URL names that are created.< / li >
< li > < code > action< / code > - the name of the current action (e.g., < code > list< / code > , < code > create< / code > ).< / li >
< li > < code > detail< / code > - boolean indicating if the current action is configured for a list or detail view.< / li >
< li > < code > suffix< / code > - the display suffix for the viewset type - mirrors the < code > detail< / code > attribute.< / li >
2018-10-11 16:48:33 +03:00
< li > < code > name< / code > - the display name for the viewset. This argument is mutually exclusive to < code > suffix< / code > .< / li >
< li > < code > description< / code > - the display description for the individual view of a viewset.< / li >
2018-04-03 16:37:24 +03:00
< / ul >
< p > You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the < code > list< / code > action similar to this:< / p >
2017-12-20 17:34:31 +03:00
< pre > < code > def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
if self.action == 'list':
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAdmin]
return [permission() for permission in permission_classes]
< / code > < / pre >
< h2 id = "marking-extra-actions-for-routing" > < a class = "toclink" href = "#marking-extra-actions-for-routing" > Marking extra actions for routing< / a > < / h2 >
2018-10-11 16:48:33 +03:00
< p > If you have ad-hoc methods that should be routable, you can mark them as such with the < code > @action< / code > decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the < code > detail< / code > argument to < code > True< / code > or < code > False< / code > . The router will configure its URL patterns accordingly. e.g., the < code > DefaultRouter< / code > will configure detail actions to contain < code > pk< / code > in their URL patterns.< / p >
2018-04-03 16:37:24 +03:00
< p > A more complete example of extra actions:< / p >
2014-11-25 19:04:38 +03:00
< pre > < code > from django.contrib.auth.models import User
2018-04-03 16:37:24 +03:00
from rest_framework import status, viewsets
from rest_framework.decorators import action
2014-11-25 19:04:38 +03:00
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
2018-10-11 16:48:33 +03:00
@action(detail=True, methods=['post'])
2014-11-25 19:04:38 +03:00
def set_password(self, request, pk=None):
user = self.get_object()
2014-12-01 15:20:07 +03:00
serializer = PasswordSerializer(data=request.data)
2014-11-25 19:04:38 +03:00
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
2018-04-03 16:37:24 +03:00
@action(detail=False)
2014-11-25 19:04:38 +03:00
def recent_users(self, request):
2018-10-11 16:48:33 +03:00
recent_users = User.objects.all().order_by('-last_login')
2015-05-14 12:03:42 +03:00
2014-11-25 19:04:38 +03:00
page = self.paginate_queryset(recent_users)
2015-05-14 12:03:42 +03:00
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(recent_users, many=True)
2014-11-25 19:04:38 +03:00
return Response(serializer.data)
< / code > < / pre >
2018-04-03 16:37:24 +03:00
< p > The decorator can additionally take extra arguments that will be set for the routed view only. For example:< / p >
2018-10-11 16:48:33 +03:00
< pre > < code > @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
2014-11-25 19:04:38 +03:00
def set_password(self, request, pk=None):
...
< / code > < / pre >
2018-10-11 16:48:33 +03:00
< p > The < code > action< / code > decorator will route < code > GET< / code > requests by default, but may also accept other HTTP methods by setting the < code > methods< / code > argument. For example:< / p >
< pre > < code > @action(detail=True, methods=['post', 'delete'])
2014-11-25 19:04:38 +03:00
def unset_password(self, request, pk=None):
...
< / code > < / pre >
< p > The two new actions will then be available at the urls < code > ^users/{pk}/set_password/$< / code > and < code > ^users/{pk}/unset_password/$< / code > < / p >
2018-04-03 16:37:24 +03:00
< p > To view all extra actions, call the < code > .get_extra_actions()< / code > method.< / p >
2018-10-11 16:48:33 +03:00
< h3 id = "routing-additional-http-methods-for-extra-actions" > < a class = "toclink" href = "#routing-additional-http-methods-for-extra-actions" > Routing additional HTTP methods for extra actions< / a > < / h3 >
< p > Extra actions can map additional HTTP methods to separate < code > ViewSet< / code > methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.< / p >
< pre > < code class = "python" > @action(detail=True, methods=['put'], name='Change Password')
def password(self, request, pk=None):
" " " Update the user's password." " "
...
@password.mapping.delete
def delete_password(self, request, pk=None):
" " " Delete the user's password." " "
...
< / code > < / pre >
2017-12-20 17:34:31 +03:00
< h2 id = "reversing-action-urls" > < a class = "toclink" href = "#reversing-action-urls" > Reversing action URLs< / a > < / h2 >
< p > If you need to get the URL of an action, use the < code > .reverse_action()< / code > method. This is a convenience wrapper for < code > reverse()< / code > , automatically passing the view's < code > request< / code > object and prepending the < code > url_name< / code > with the < code > .basename< / code > attribute.< / p >
< p > Note that the < code > basename< / code > is provided by the router during < code > ViewSet< / code > registration. If you are not using a router, then you must provide the < code > basename< / code > argument to the < code > .as_view()< / code > method.< / p >
< p > Using the example from the previous section:< / p >
< pre > < code class = "python" > > > > view.reverse_action('set-password', args=['1'])
'http://localhost:8000/api/users/1/set_password'
< / code > < / pre >
2018-04-03 16:37:24 +03:00
< p > Alternatively, you can use the < code > url_name< / code > attribute set by the < code > @action< / code > decorator.< / p >
< pre > < code class = "python" > > > > view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
< / code > < / pre >
< p > The < code > url_name< / code > argument for < code > .reverse_action()< / code > should match the same argument to the < code > @action< / code > decorator. Additionally, this method can be used to reverse the default actions, such as < code > list< / code > and < code > create< / code > .< / p >
2014-11-25 19:04:38 +03:00
< hr / >
2015-11-04 17:59:23 +03:00
< h1 id = "api-reference" > < a class = "toclink" href = "#api-reference" > API Reference< / a > < / h1 >
< h2 id = "viewset" > < a class = "toclink" href = "#viewset" > ViewSet< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > The < code > ViewSet< / code > class inherits from < code > APIView< / code > . You can use any of the standard attributes such as < code > permission_classes< / code > , < code > authentication_classes< / code > in order to control the API policy on the viewset.< / p >
< p > The < code > ViewSet< / code > class does not provide any implementations of actions. In order to use a < code > ViewSet< / code > class you'll override the class and define the action implementations explicitly.< / p >
2015-11-04 17:59:23 +03:00
< h2 id = "genericviewset" > < a class = "toclink" href = "#genericviewset" > GenericViewSet< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > The < code > GenericViewSet< / code > class inherits from < code > GenericAPIView< / code > , and provides the default set of < code > get_object< / code > , < code > get_queryset< / code > methods and other generic view base behavior, but does not include any actions by default.< / p >
< p > In order to use a < code > GenericViewSet< / code > class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.< / p >
2015-11-04 17:59:23 +03:00
< h2 id = "modelviewset" > < a class = "toclink" href = "#modelviewset" > ModelViewSet< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > The < code > ModelViewSet< / code > class inherits from < code > GenericAPIView< / code > and includes implementations for various actions, by mixing in the behavior of the various mixin classes.< / p >
2016-05-26 13:48:39 +03:00
< p > The actions provided by the < code > ModelViewSet< / code > class are < code > .list()< / code > , < code > .retrieve()< / code > , < code > .create()< / code > , < code > .update()< / code > , < code > .partial_update()< / code > , and < code > .destroy()< / code > .< / p >
2015-11-04 17:59:23 +03:00
< h4 id = "example_1" > < a class = "toclink" href = "#example_1" > Example< / a > < / h4 >
2015-03-06 15:05:16 +03:00
< p > Because < code > ModelViewSet< / code > extends < code > GenericAPIView< / code > , you'll normally need to provide at least the < code > queryset< / code > and < code > serializer_class< / code > attributes. For example:< / p >
2014-11-25 19:04:38 +03:00
< pre > < code > class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
< / code > < / pre >
< p > Note that you can use any of the standard attributes or method overrides provided by < code > GenericAPIView< / code > . For example, to use a < code > ViewSet< / code > that dynamically determines the queryset it should operate on, you might do something like this:< / p >
< pre > < code > class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing the accounts
associated with the user.
"""
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
def get_queryset(self):
return self.request.user.accounts.all()
< / code > < / pre >
2018-10-11 16:48:33 +03:00
< p > Note however that upon removal of the < code > queryset< / code > property from your < code > ViewSet< / code > , any associated < a href = "../routers/" > router< / a > will be unable to derive the basename of your Model automatically, and so you will have to specify the < code > basename< / code > kwarg as part of your < a href = "../routers/" > router registration< / a > .< / p >
2014-11-25 19:04:38 +03:00
< p > Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.< / p >
2015-11-04 17:59:23 +03:00
< h2 id = "readonlymodelviewset" > < a class = "toclink" href = "#readonlymodelviewset" > ReadOnlyModelViewSet< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > The < code > ReadOnlyModelViewSet< / code > class also inherits from < code > GenericAPIView< / code > . As with < code > ModelViewSet< / code > it also includes implementations for various actions, but unlike < code > ModelViewSet< / code > only provides the 'read-only' actions, < code > .list()< / code > and < code > .retrieve()< / code > .< / p >
2015-11-04 17:59:23 +03:00
< h4 id = "example_2" > < a class = "toclink" href = "#example_2" > Example< / a > < / h4 >
2014-11-25 19:04:38 +03:00
< p > As with < code > ModelViewSet< / code > , you'll normally need to provide at least the < code > queryset< / code > and < code > serializer_class< / code > attributes. For example:< / p >
< pre > < code > class AccountViewSet(viewsets.ReadOnlyModelViewSet):
"""
A simple ViewSet for viewing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
< / code > < / pre >
< p > Again, as with < code > ModelViewSet< / code > , you can use any of the standard attributes and method overrides available to < code > GenericAPIView< / code > .< / p >
2015-11-04 17:59:23 +03:00
< h1 id = "custom-viewset-base-classes" > < a class = "toclink" href = "#custom-viewset-base-classes" > Custom ViewSet base classes< / a > < / h1 >
2014-11-25 19:04:38 +03:00
< p > You may need to provide custom < code > ViewSet< / code > classes that do not have the full set of < code > ModelViewSet< / code > actions, or that customize the behavior in some other way.< / p >
2015-11-04 17:59:23 +03:00
< h2 id = "example_3" > < a class = "toclink" href = "#example_3" > Example< / a > < / h2 >
2014-11-25 19:04:38 +03:00
< p > To create a base viewset class that provides < code > create< / code > , < code > list< / code > and < code > retrieve< / code > operations, inherit from < code > GenericViewSet< / code > , and mixin the required actions:< / p >
2017-03-27 13:20:12 +03:00
< pre > < code > from rest_framework import mixins
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
2014-11-25 19:04:38 +03:00
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
A viewset that provides `retrieve`, `create`, and `list` actions.
To use it, override the class and set the `.queryset` and
`.serializer_class` attributes.
"""
pass
< / code > < / pre >
< p > By creating your own base < code > ViewSet< / code > classes, you can provide common behavior that can be reused in multiple viewsets across your API.< / p >
2015-06-04 17:37:22 +03:00
2015-06-23 14:05:53 +03:00
< / div > <!-- /span -->
< / div > <!-- /row -->
< / div > <!-- /.fluid - container -->
< / div > <!-- /.body content -->
2014-11-25 19:04:38 +03:00
< div id = "push" > < / div >
2015-06-23 14:05:53 +03:00
< / div > <!-- /.wrapper -->
2014-11-25 19:04:38 +03:00
< footer class = "span12" >
2015-10-28 14:35:39 +03:00
< p > Documentation built with < a href = "http://www.mkdocs.org/" > MkDocs< / a > .
2014-11-25 19:04:38 +03:00
< / p >
< / footer >
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
< script src = "../../js/jquery-1.8.1-min.js" > < / script >
< script src = "../../js/prettify-1.0.js" > < / script >
< script src = "../../js/bootstrap-2.1.1-min.js" > < / script >
2019-06-04 14:04:28 +03:00
< script async src = "https://fund.django-rest-framework.org/sidebar_include.js" > < / script >
2015-06-04 17:37:22 +03:00
< script > var base _url = '../..' ; < / script >
< script src = "../../mkdocs/js/require.js" > < / script >
2014-12-11 12:55:10 +03:00
< script src = "../../js/theme.js" > < / script >
2014-11-25 19:04:38 +03:00
< script >
var shiftWindow = function() {
scrollBy(0, -50)
};
2015-06-23 14:05:53 +03:00
2014-11-25 19:04:38 +03:00
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
$('.dropdown-menu').on('click touchstart', function(event) {
event.stopPropagation();
});
2015-06-23 14:05:53 +03:00
// Dynamically force sidenav/dropdown to no higher than browser window
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
2014-11-25 19:04:38 +03:00
$(function() {
$(window).resize(function() {
2015-06-23 14:05:53 +03:00
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
2014-11-25 19:04:38 +03:00
});
});
< / script >
< / body >
< / html >