mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-23 01:57:00 +03:00
Drop resources from codebase since implementation is only partial (Created resoorces-routers branch for future reference)
This commit is contained in:
parent
c7a916a979
commit
195006bbc3
|
@ -1,76 +0,0 @@
|
||||||
# Tutorial 6 - Resources
|
|
||||||
|
|
||||||
Resource classes are just View classes that don't have any handler methods bound to them. The actions on a resource are defined,
|
|
||||||
|
|
||||||
This allows us to:
|
|
||||||
|
|
||||||
* Encapsulate common behaviour across a class of views, in a single Resource class.
|
|
||||||
* Separate out the actions of a Resource from the specifics of how those actions should be bound to a particular set of URLs.
|
|
||||||
|
|
||||||
## Refactoring to use Resources, not Views
|
|
||||||
|
|
||||||
For instance, we can re-write our 4 sets of views into something more compact...
|
|
||||||
|
|
||||||
resources.py
|
|
||||||
|
|
||||||
class BlogPostResource(ModelResource):
|
|
||||||
serializer_class = BlogPostSerializer
|
|
||||||
model = BlogPost
|
|
||||||
permissions_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
|
||||||
throttle_classes = (throttles.UserRateThrottle,)
|
|
||||||
|
|
||||||
class CommentResource(ModelResource):
|
|
||||||
serializer_class = CommentSerializer
|
|
||||||
model = Comment
|
|
||||||
permissions_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
|
||||||
throttle_classes = (throttles.UserRateThrottle,)
|
|
||||||
|
|
||||||
## Binding Resources to URLs explicitly
|
|
||||||
The handler methods only get bound to the actions when we define the URLConf. Here's our urls.py:
|
|
||||||
|
|
||||||
comment_root = CommentResource.as_view(actions={
|
|
||||||
'get': 'list',
|
|
||||||
'post': 'create'
|
|
||||||
})
|
|
||||||
comment_instance = CommentInstance.as_view(actions={
|
|
||||||
'get': 'retrieve',
|
|
||||||
'put': 'update',
|
|
||||||
'delete': 'destroy'
|
|
||||||
})
|
|
||||||
... # And for blog post
|
|
||||||
|
|
||||||
urlpatterns = patterns('blogpost.views',
|
|
||||||
url(r'^$', comment_root),
|
|
||||||
url(r'^(?P<pk>[0-9]+)$', comment_instance)
|
|
||||||
... # And for blog post
|
|
||||||
)
|
|
||||||
|
|
||||||
## Using Routers
|
|
||||||
|
|
||||||
Right now that hasn't really saved us a lot of code. However, now that we're using Resources rather than Views, we actually don't need to design the urlconf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using `Router` classes. All we need to do is register the appropriate resources with a router, and let it do the rest. Here's our re-wired `urls.py` file.
|
|
||||||
|
|
||||||
from blog import resources
|
|
||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
|
||||||
router.register(resources.BlogPostResource)
|
|
||||||
router.register(resources.CommentResource)
|
|
||||||
urlpatterns = router.urlpatterns
|
|
||||||
|
|
||||||
## Trade-offs between views vs resources.
|
|
||||||
|
|
||||||
Writing resource-oriented code can be a good thing. It helps ensure that URL conventions will be consistent across your APIs, and minimises the amount of code you need to write.
|
|
||||||
|
|
||||||
The trade-off is that the behaviour is less explict. It can be more difficult to determine what code path is being followed, or where to override some behaviour.
|
|
||||||
|
|
||||||
## Onwards and upwards.
|
|
||||||
|
|
||||||
We've reached the end of our tutorial. If you want to get more involved in the REST framework project, here's a few places you can start:
|
|
||||||
|
|
||||||
* Contribute on GitHub by reviewing issues, and submitting issues or pull requests.
|
|
||||||
* Join the REST framework group, and help build the community.
|
|
||||||
* Follow me [on Twitter][twitter] and say hi.
|
|
||||||
|
|
||||||
**Now go build some awesome things.**
|
|
||||||
|
|
||||||
[twitter]: https://twitter.com/_tomchristie
|
|
|
@ -1,96 +0,0 @@
|
||||||
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
|
||||||
|
|
||||||
from functools import update_wrapper
|
|
||||||
import inspect
|
|
||||||
from django.utils.decorators import classonlymethod
|
|
||||||
from rest_framework import views, generics
|
|
||||||
|
|
||||||
|
|
||||||
def wrapped(source, dest):
|
|
||||||
"""
|
|
||||||
Copy public, non-method attributes from source to dest, and return dest.
|
|
||||||
"""
|
|
||||||
for attr in [attr for attr in dir(source)
|
|
||||||
if not attr.startswith('_') and not inspect.ismethod(attr)]:
|
|
||||||
setattr(dest, attr, getattr(source, attr))
|
|
||||||
return dest
|
|
||||||
|
|
||||||
|
|
||||||
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
|
||||||
|
|
||||||
class ResourceMixin(object):
|
|
||||||
"""
|
|
||||||
Clone Django's `View.as_view()` behaviour *except* using REST framework's
|
|
||||||
'method -> action' binding for resources.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classonlymethod
|
|
||||||
def as_view(cls, actions, **initkwargs):
|
|
||||||
"""
|
|
||||||
Main entry point for a request-response process.
|
|
||||||
"""
|
|
||||||
# sanitize keyword arguments
|
|
||||||
for key in initkwargs:
|
|
||||||
if key in cls.http_method_names:
|
|
||||||
raise TypeError("You tried to pass in the %s method name as a "
|
|
||||||
"keyword argument to %s(). Don't do that."
|
|
||||||
% (key, cls.__name__))
|
|
||||||
if not hasattr(cls, key):
|
|
||||||
raise TypeError("%s() received an invalid keyword %r" % (
|
|
||||||
cls.__name__, key))
|
|
||||||
|
|
||||||
def view(request, *args, **kwargs):
|
|
||||||
self = cls(**initkwargs)
|
|
||||||
|
|
||||||
# Bind methods to actions
|
|
||||||
for method, action in actions.items():
|
|
||||||
handler = getattr(self, action)
|
|
||||||
setattr(self, method, handler)
|
|
||||||
|
|
||||||
# As you were, solider.
|
|
||||||
if hasattr(self, 'get') and not hasattr(self, 'head'):
|
|
||||||
self.head = self.get
|
|
||||||
return self.dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
# take name and docstring from class
|
|
||||||
update_wrapper(view, cls, updated=())
|
|
||||||
|
|
||||||
# and possible attributes set by decorators
|
|
||||||
# like csrf_exempt from dispatch
|
|
||||||
update_wrapper(view, cls.dispatch, assigned=())
|
|
||||||
return view
|
|
||||||
|
|
||||||
|
|
||||||
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
|
||||||
|
|
||||||
class Resource(ResourceMixin, views.APIView):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
|
||||||
|
|
||||||
class ModelResource(ResourceMixin, views.APIView):
|
|
||||||
# TODO: Actually delegation won't work
|
|
||||||
root_class = generics.ListCreateAPIView
|
|
||||||
detail_class = generics.RetrieveUpdateDestroyAPIView
|
|
||||||
|
|
||||||
def root_view(self):
|
|
||||||
return wrapped(self, self.root_class())
|
|
||||||
|
|
||||||
def detail_view(self):
|
|
||||||
return wrapped(self, self.detail_class())
|
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
|
||||||
return self.root_view().list(request, args, kwargs)
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
return self.root_view().create(request, args, kwargs)
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
return self.detail_view().retrieve(request, args, kwargs)
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
|
||||||
return self.detail_view().update(request, args, kwargs)
|
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
return self.detail_view().destroy(request, args, kwargs)
|
|
Loading…
Reference in New Issue
Block a user