Drop resources from codebase since implementation is only partial (Created resoorces-routers branch for future reference)

This commit is contained in:
Tom Christie 2012-10-26 09:27:59 +01:00
parent c7a916a979
commit 195006bbc3
2 changed files with 0 additions and 172 deletions

View File

@ -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

View File

@ -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)