Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
8fa79a7f
Commit
8fa79a7f
authored
Apr 26, 2013
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Deal with List/Instance suffixes for viewsets
parent
e301e2d9
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
62 additions
and
44 deletions
+62
-44
docs/api-guide/routers.md
+3
-3
docs/api-guide/viewsets.md
+1
-1
docs/tutorial/6-viewsets-and-routers.md
+2
-2
rest_framework/renderers.py
+1
-1
rest_framework/routers.py
+39
-33
rest_framework/utils/breadcrumbs.py
+2
-1
rest_framework/utils/formatting.py
+5
-2
rest_framework/viewsets.py
+9
-1
No files found.
docs/api-guide/routers.md
View file @
8fa79a7f
...
@@ -15,15 +15,15 @@ REST framework adds support for automatic URL routing to Django, and provides yo
...
@@ -15,15 +15,15 @@ REST framework adds support for automatic URL routing to Django, and provides yo
Here's an example of a simple URL conf, that uses
`DefaultRouter`
.
Here's an example of a simple URL conf, that uses
`DefaultRouter`
.
router = routers.SimpleRouter()
router = routers.SimpleRouter()
router.register(r'users', UserViewSet, 'user')
router.register(r'users', UserViewSet,
name=
'user')
router.register(r'accounts', AccountViewSet, 'account')
router.register(r'accounts', AccountViewSet,
name=
'account')
urlpatterns = router.urls
urlpatterns = router.urls
There are three arguments to the
`register()`
method:
There are three arguments to the
`register()`
method:
*
`prefix`
- The URL prefix to use for this set of routes.
*
`prefix`
- The URL prefix to use for this set of routes.
*
`viewset`
- The viewset class.
*
`viewset`
- The viewset class.
*
`
base
name`
- The base to use for the URL names that are created.
*
`name`
- The base to use for the URL names that are created.
The example above would generate the following URL patterns:
The example above would generate the following URL patterns:
...
...
docs/api-guide/viewsets.md
View file @
8fa79a7f
...
@@ -42,7 +42,7 @@ If we need to, we can bind this viewset into two seperate views, like so:
...
@@ -42,7 +42,7 @@ If we need to, we can bind this viewset into two seperate views, like so:
Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.
Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.
router = DefaultRouter()
router = DefaultRouter()
router.register(r'users', UserViewSet, 'user')
router.register(r'users', UserViewSet,
name=
'user')
urlpatterns = router.urls
urlpatterns = router.urls
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:
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:
...
...
docs/tutorial/6-viewsets-and-routers.md
View file @
8fa79a7f
...
@@ -105,8 +105,8 @@ Here's our re-wired `urls.py` file.
...
@@ -105,8 +105,8 @@ Here's our re-wired `urls.py` file.
# Create a router and register our viewsets with it.
# Create a router and register our viewsets with it.
router = DefaultRouter()
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet, 'snippet')
router.register(r'snippets', views.SnippetViewSet,
name=
'snippet')
router.register(r'users', views.UserViewSet, 'user')
router.register(r'users', views.UserViewSet,
name=
'user')
# The API URLs are now determined automatically by the router.
# The API URLs are now determined automatically by the router.
# Additionally, we include the login URLs for the browseable API.
# Additionally, we include the login URLs for the browseable API.
...
...
rest_framework/renderers.py
View file @
8fa79a7f
...
@@ -439,7 +439,7 @@ class BrowsableAPIRenderer(BaseRenderer):
...
@@ -439,7 +439,7 @@ class BrowsableAPIRenderer(BaseRenderer):
return
GenericContentForm
()
return
GenericContentForm
()
def
get_name
(
self
,
view
):
def
get_name
(
self
,
view
):
return
get_view_name
(
view
.
__class__
)
return
get_view_name
(
view
.
__class__
,
getattr
(
view
,
'suffix'
,
None
)
)
def
get_description
(
self
,
view
):
def
get_description
(
self
,
view
):
return
get_view_description
(
view
.
__class__
,
html
=
True
)
return
get_view_description
(
view
.
__class__
,
html
=
True
)
...
...
rest_framework/routers.py
View file @
8fa79a7f
...
@@ -13,6 +13,7 @@ For example, you might have a `urls.py` that looks something like this:
...
@@ -13,6 +13,7 @@ For example, you might have a `urls.py` that looks something like this:
urlpatterns = router.urls
urlpatterns = router.urls
"""
"""
from
collections
import
namedtuple
from
django.conf.urls
import
url
,
patterns
from
django.conf.urls
import
url
,
patterns
from
django.db
import
models
from
django.db
import
models
from
rest_framework.decorators
import
api_view
from
rest_framework.decorators
import
api_view
...
@@ -22,6 +23,9 @@ from rest_framework.viewsets import ModelViewSet
...
@@ -22,6 +23,9 @@ from rest_framework.viewsets import ModelViewSet
from
rest_framework.urlpatterns
import
format_suffix_patterns
from
rest_framework.urlpatterns
import
format_suffix_patterns
Route
=
namedtuple
(
'Route'
,
[
'url'
,
'mapping'
,
'name'
,
'initkwargs'
])
def
replace_methodname
(
format_string
,
methodname
):
def
replace_methodname
(
format_string
,
methodname
):
"""
"""
Partially format a format_string, swapping out any
Partially format a format_string, swapping out any
...
@@ -38,8 +42,8 @@ class BaseRouter(object):
...
@@ -38,8 +42,8 @@ class BaseRouter(object):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
registry
=
[]
self
.
registry
=
[]
def
register
(
self
,
prefix
,
viewset
,
base
name
):
def
register
(
self
,
prefix
,
viewset
,
name
):
self
.
registry
.
append
((
prefix
,
viewset
,
base
name
))
self
.
registry
.
append
((
prefix
,
viewset
,
name
))
def
get_urls
(
self
):
def
get_urls
(
self
):
raise
NotImplemented
(
'get_urls must be overridden'
)
raise
NotImplemented
(
'get_urls must be overridden'
)
...
@@ -54,33 +58,36 @@ class BaseRouter(object):
...
@@ -54,33 +58,36 @@ class BaseRouter(object):
class
SimpleRouter
(
BaseRouter
):
class
SimpleRouter
(
BaseRouter
):
routes
=
[
routes
=
[
# List route.
# List route.
(
Route
(
r'^{prefix}/$'
,
url
=
r'^{prefix}/$'
,
{
mapping
=
{
'get'
:
'list'
,
'get'
:
'list'
,
'post'
:
'create'
'post'
:
'create'
},
},
'{basename}-list'
name
=
'{basename}-list'
,
initkwargs
=
{
'suffix'
:
'List'
}
),
),
# Detail route.
# Detail route.
(
Route
(
r'^{prefix}/{lookup}/$'
,
url
=
r'^{prefix}/{lookup}/$'
,
{
mapping
=
{
'get'
:
'retrieve'
,
'get'
:
'retrieve'
,
'put'
:
'update'
,
'put'
:
'update'
,
'patch'
:
'partial_update'
,
'patch'
:
'partial_update'
,
'delete'
:
'destroy'
'delete'
:
'destroy'
},
},
'{basename}-detail'
name
=
'{basename}-detail'
,
initkwargs
=
{
'suffix'
:
'Instance'
}
),
),
# Dynamically generated routes.
# Dynamically generated routes.
# Generated using @action or @link decorators on methods of the viewset.
# Generated using @action or @link decorators on methods of the viewset.
(
Route
(
r'^{prefix}/{lookup}/{methodname}/$'
,
url
=
r'^{prefix}/{lookup}/{methodname}/$'
,
{
mapping
=
{
'{httpmethod}'
:
'{methodname}'
,
'{httpmethod}'
:
'{methodname}'
,
},
},
'{basename}-{methodnamehyphen}'
name
=
'{basename}-{methodnamehyphen}'
,
initkwargs
=
{}
),
),
]
]
...
@@ -88,8 +95,7 @@ class SimpleRouter(BaseRouter):
...
@@ -88,8 +95,7 @@ class SimpleRouter(BaseRouter):
"""
"""
Augment `self.routes` with any dynamically generated routes.
Augment `self.routes` with any dynamically generated routes.
Returns a list of 4-tuples, of the form:
Returns a list of the Route namedtuple.
`(url_format, method_map, name_format, extra_kwargs)`
"""
"""
# Determine any `@action` or `@link` decorated methods on the viewset
# Determine any `@action` or `@link` decorated methods on the viewset
...
@@ -101,21 +107,21 @@ class SimpleRouter(BaseRouter):
...
@@ -101,21 +107,21 @@ class SimpleRouter(BaseRouter):
dynamic_routes
[
httpmethod
]
=
methodname
dynamic_routes
[
httpmethod
]
=
methodname
ret
=
[]
ret
=
[]
for
url_format
,
method_map
,
name_format
in
self
.
routes
:
for
route
in
self
.
routes
:
if
method_map
==
{
'{httpmethod}'
:
'{methodname}'
}:
if
route
.
mapping
==
{
'{httpmethod}'
:
'{methodname}'
}:
# Dynamic routes (@link or @action decorator)
# Dynamic routes (@link or @action decorator)
for
httpmethod
,
methodname
in
dynamic_routes
.
items
():
for
httpmethod
,
methodname
in
dynamic_routes
.
items
():
extra_kwargs
=
getattr
(
viewset
,
methodname
)
.
kwargs
initkwargs
=
route
.
initkwargs
.
copy
()
ret
.
append
((
initkwargs
.
update
(
getattr
(
viewset
,
methodname
)
.
kwargs
)
replace_methodname
(
url_format
,
methodname
),
ret
.
append
(
Route
(
{
httpmethod
:
methodname
},
url
=
replace_methodname
(
route
.
url
,
methodname
),
replace_methodname
(
name_format
,
methodname
),
mapping
=
{
httpmethod
:
methodname
},
extra_kwargs
name
=
replace_methodname
(
route
.
name
,
methodname
),
initkwargs
=
initkwargs
,
))
))
else
:
else
:
# Standard route
# Standard route
extra_kwargs
=
{}
ret
.
append
(
route
)
ret
.
append
((
url_format
,
method_map
,
name_format
,
extra_kwargs
))
return
ret
return
ret
...
@@ -150,17 +156,17 @@ class SimpleRouter(BaseRouter):
...
@@ -150,17 +156,17 @@ class SimpleRouter(BaseRouter):
lookup
=
self
.
get_lookup_regex
(
viewset
)
lookup
=
self
.
get_lookup_regex
(
viewset
)
routes
=
self
.
get_routes
(
viewset
)
routes
=
self
.
get_routes
(
viewset
)
for
url_format
,
method_map
,
name_format
,
extra_kwargs
in
routes
:
for
route
in
routes
:
# Only actions which actually exist on the viewset will be bound
# Only actions which actually exist on the viewset will be bound
m
ethod_map
=
self
.
get_method_map
(
viewset
,
method_map
)
m
apping
=
self
.
get_method_map
(
viewset
,
route
.
mapping
)
if
not
m
ethod_map
:
if
not
m
apping
:
continue
continue
# Build the url pattern
# Build the url pattern
regex
=
url_format
.
format
(
prefix
=
prefix
,
lookup
=
lookup
)
regex
=
route
.
url
.
format
(
prefix
=
prefix
,
lookup
=
lookup
)
view
=
viewset
.
as_view
(
m
ethod_map
,
**
extra_
kwargs
)
view
=
viewset
.
as_view
(
m
apping
,
**
route
.
init
kwargs
)
name
=
name_format
.
format
(
basename
=
basename
)
name
=
route
.
name
.
format
(
basename
=
basename
)
ret
.
append
(
url
(
regex
,
view
,
name
=
name
))
ret
.
append
(
url
(
regex
,
view
,
name
=
name
))
return
ret
return
ret
...
@@ -179,7 +185,7 @@ class DefaultRouter(SimpleRouter):
...
@@ -179,7 +185,7 @@ class DefaultRouter(SimpleRouter):
Return a view to use as the API root.
Return a view to use as the API root.
"""
"""
api_root_dict
=
{}
api_root_dict
=
{}
list_name
=
self
.
routes
[
0
]
[
-
1
]
list_name
=
self
.
routes
[
0
]
.
name
for
prefix
,
viewset
,
basename
in
self
.
registry
:
for
prefix
,
viewset
,
basename
in
self
.
registry
:
api_root_dict
[
prefix
]
=
list_name
.
format
(
basename
=
basename
)
api_root_dict
[
prefix
]
=
list_name
.
format
(
basename
=
basename
)
...
...
rest_framework/utils/breadcrumbs.py
View file @
8fa79a7f
...
@@ -21,7 +21,8 @@ def get_breadcrumbs(url):
...
@@ -21,7 +21,8 @@ def get_breadcrumbs(url):
# Don't list the same view twice in a row.
# Don't list the same view twice in a row.
# Probably an optional trailing slash.
# Probably an optional trailing slash.
if
not
seen
or
seen
[
-
1
]
!=
view
:
if
not
seen
or
seen
[
-
1
]
!=
view
:
breadcrumbs_list
.
insert
(
0
,
(
get_view_name
(
view
.
cls
),
prefix
+
url
))
suffix
=
getattr
(
view
,
'suffix'
,
None
)
breadcrumbs_list
.
insert
(
0
,
(
get_view_name
(
view
.
cls
,
suffix
),
prefix
+
url
))
seen
.
append
(
view
)
seen
.
append
(
view
)
if
url
==
''
:
if
url
==
''
:
...
...
rest_framework/utils/formatting.py
View file @
8fa79a7f
...
@@ -45,14 +45,17 @@ def _camelcase_to_spaces(content):
...
@@ -45,14 +45,17 @@ def _camelcase_to_spaces(content):
return
' '
.
join
(
content
.
split
(
'_'
))
.
title
()
return
' '
.
join
(
content
.
split
(
'_'
))
.
title
()
def
get_view_name
(
cls
):
def
get_view_name
(
cls
,
suffix
=
None
):
"""
"""
Return a formatted name for an `APIView` class or `@api_view` function.
Return a formatted name for an `APIView` class or `@api_view` function.
"""
"""
name
=
cls
.
__name__
name
=
cls
.
__name__
name
=
_remove_trailing_string
(
name
,
'View'
)
name
=
_remove_trailing_string
(
name
,
'View'
)
name
=
_remove_trailing_string
(
name
,
'ViewSet'
)
name
=
_remove_trailing_string
(
name
,
'ViewSet'
)
return
_camelcase_to_spaces
(
name
)
name
=
_camelcase_to_spaces
(
name
)
if
suffix
:
name
+=
' '
+
suffix
return
name
def
get_view_description
(
cls
,
html
=
False
):
def
get_view_description
(
cls
,
html
=
False
):
...
...
rest_framework/viewsets.py
View file @
8fa79a7f
...
@@ -35,12 +35,16 @@ class ViewSetMixin(object):
...
@@ -35,12 +35,16 @@ class ViewSetMixin(object):
"""
"""
@classonlymethod
@classonlymethod
def
as_view
(
cls
,
actions
=
None
,
name_suffix
=
None
,
**
initkwargs
):
def
as_view
(
cls
,
actions
=
None
,
**
initkwargs
):
"""
"""
Because of the way class based views create a closure around the
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
and slightly modify the view function that is created and returned.
"""
"""
# The suffix initkwarg is reserved for identifing the viewset type
# eg. 'List' or 'Instance'.
cls
.
suffix
=
None
# sanitize keyword arguments
# sanitize keyword arguments
for
key
in
initkwargs
:
for
key
in
initkwargs
:
if
key
in
cls
.
http_method_names
:
if
key
in
cls
.
http_method_names
:
...
@@ -74,7 +78,11 @@ class ViewSetMixin(object):
...
@@ -74,7 +78,11 @@ class ViewSetMixin(object):
# like csrf_exempt from dispatch
# like csrf_exempt from dispatch
update_wrapper
(
view
,
cls
.
dispatch
,
assigned
=
())
update_wrapper
(
view
,
cls
.
dispatch
,
assigned
=
())
# We need to set these on the view function, so that breadcrumb
# generation can pick out these bits of information from a
# resolved URL.
view
.
cls
=
cls
view
.
cls
=
cls
view
.
suffix
=
initkwargs
.
get
(
'suffix'
,
None
)
return
view
return
view
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment