-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Yowzers. Final big bunch of refactoring for 0.1 release. Now support …
…Django 1.3's views, admin style api is all polished off, loads of tests, new test project for running the test. All sorts of goodness. Getting ready to push this out now.
- Loading branch information
tom christie tom@tomchristie.com
committed
Feb 19, 2011
1 parent
b749b95
commit 805aa03
Showing
58 changed files
with
1,716 additions
and
717 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Project Owner... | ||
|
||
Tom Christie <tomchristie> - tom@tomchristie.com | ||
|
||
Thanks to... | ||
|
||
Jesper Noehr <jespern> & the django-piston contributors for providing the starting point for this project. | ||
Paul Bagwell <pbgwl> - Suggestions & bugfixes. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from django.core.urlresolvers import resolve | ||
from djangorestframework.description import get_name | ||
|
||
def get_breadcrumbs(url): | ||
"""Given a url returns a list of breadcrumbs, which are each a tuple of (name, url).""" | ||
|
||
def breadcrumbs_recursive(url, breadcrumbs_list): | ||
"""Add tuples of (name, url) to the breadcrumbs list, progressively chomping off parts of the url.""" | ||
|
||
# This is just like compsci 101 all over again... | ||
try: | ||
(view, unused_args, unused_kwargs) = resolve(url) | ||
except: | ||
pass | ||
else: | ||
if callable(view): | ||
breadcrumbs_list.insert(0, (get_name(view), url)) | ||
|
||
if url == '': | ||
# All done | ||
return breadcrumbs_list | ||
|
||
elif url.endswith('/'): | ||
# Drop trailing slash off the end and continue to try to resolve more breadcrumbs | ||
return breadcrumbs_recursive(url.rstrip('/'), breadcrumbs_list) | ||
|
||
# Drop trailing non-slash off the end and continue to try to resolve more breadcrumbs | ||
return breadcrumbs_recursive(url[:url.rfind('/') + 1], breadcrumbs_list) | ||
|
||
return breadcrumbs_recursive(url, []) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
"""Compatability module to provide support for backwards compatability with older versions of django/python""" | ||
|
||
# django.test.client.RequestFactory (Django >= 1.3) | ||
try: | ||
from django.test.client import RequestFactory | ||
|
||
except ImportError: | ||
from django.test import Client | ||
from django.core.handlers.wsgi import WSGIRequest | ||
|
||
# From: http://djangosnippets.org/snippets/963/ | ||
# Lovely stuff | ||
class RequestFactory(Client): | ||
""" | ||
Class that lets you create mock Request objects for use in testing. | ||
Usage: | ||
rf = RequestFactory() | ||
get_request = rf.get('/hello/') | ||
post_request = rf.post('/submit/', {'foo': 'bar'}) | ||
This class re-uses the django.test.client.Client interface, docs here: | ||
http://www.djangoproject.com/documentation/testing/#the-test-client | ||
Once you have a request object you can pass it to any view function, | ||
just as if that view had been hooked up using a URLconf. | ||
""" | ||
def request(self, **request): | ||
""" | ||
Similar to parent class, but returns the request object as soon as it | ||
has created it. | ||
""" | ||
environ = { | ||
'HTTP_COOKIE': self.cookies, | ||
'PATH_INFO': '/', | ||
'QUERY_STRING': '', | ||
'REQUEST_METHOD': 'GET', | ||
'SCRIPT_NAME': '', | ||
'SERVER_NAME': 'testserver', | ||
'SERVER_PORT': 80, | ||
'SERVER_PROTOCOL': 'HTTP/1.1', | ||
} | ||
environ.update(self.defaults) | ||
environ.update(request) | ||
return WSGIRequest(environ) | ||
|
||
# django.views.generic.View (Django >= 1.3) | ||
try: | ||
from django.views.generic import View | ||
except: | ||
from django import http | ||
from django.utils.functional import update_wrapper | ||
# from django.utils.log import getLogger | ||
# from django.utils.decorators import classonlymethod | ||
|
||
# logger = getLogger('django.request') - We'll just drop support for logger if running Django <= 1.2 | ||
# Might be nice to fix this up sometime to allow djangorestframework.compat.View to match 1.3's View more closely | ||
|
||
class View(object): | ||
""" | ||
Intentionally simple parent class for all views. Only implements | ||
dispatch-by-method and simple sanity checking. | ||
""" | ||
|
||
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace'] | ||
|
||
def __init__(self, **kwargs): | ||
""" | ||
Constructor. Called in the URLconf; can contain helpful extra | ||
keyword arguments, and other things. | ||
""" | ||
# Go through keyword arguments, and either save their values to our | ||
# instance, or raise an error. | ||
for key, value in kwargs.iteritems(): | ||
setattr(self, key, value) | ||
|
||
# @classonlymethod - We'll just us classmethod instead if running Django <= 1.2 | ||
@classmethod | ||
def as_view(cls, **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(u"You tried to pass in the %s method name as a " | ||
u"keyword argument to %s(). Don't do that." | ||
% (key, cls.__name__)) | ||
if not hasattr(cls, key): | ||
raise TypeError(u"%s() received an invalid keyword %r" % ( | ||
cls.__name__, key)) | ||
|
||
def view(request, *args, **kwargs): | ||
self = cls(**initkwargs) | ||
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 | ||
|
||
def dispatch(self, request, *args, **kwargs): | ||
# Try to dispatch to the right method; if a method doesn't exist, | ||
# defer to the error handler. Also defer to the error handler if the | ||
# request method isn't on the approved list. | ||
if request.method.lower() in self.http_method_names: | ||
handler = getattr(self, request.method.lower(), self.http_method_not_allowed) | ||
else: | ||
handler = self.http_method_not_allowed | ||
self.request = request | ||
self.args = args | ||
self.kwargs = kwargs | ||
return handler(request, *args, **kwargs) | ||
|
||
def http_method_not_allowed(self, request, *args, **kwargs): | ||
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)] | ||
#logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path), | ||
# extra={ | ||
# 'status_code': 405, | ||
# 'request': self.request | ||
# } | ||
#) | ||
return http.HttpResponseNotAllowed(allowed_methods) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"""Get a descriptive name and description for a view, | ||
based on class name and docstring, and override-able by 'name' and 'description' attributes""" | ||
import re | ||
|
||
def get_name(view): | ||
"""Return a name for the view. | ||
If view has a name attribute, use that, otherwise use the view's class name, with 'CamelCaseNames' converted to 'Camel Case Names'.""" | ||
if getattr(view, 'name', None) is not None: | ||
return view.name | ||
|
||
if getattr(view, '__name__', None) is not None: | ||
name = view.__name__ | ||
elif getattr(view, '__class__', None) is not None: # TODO: should be able to get rid of this case once refactoring to 1.3 class views is complete | ||
name = view.__class__.__name__ | ||
else: | ||
return '' | ||
|
||
return re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', name).strip() | ||
|
||
def get_description(view): | ||
"""Provide a description for the view. | ||
By default this is the view's docstring with nice unindention applied.""" | ||
if getattr(view, 'description', None) is not None: | ||
return getattr(view, 'description') | ||
|
||
if getattr(view, '__doc__', None) is not None: | ||
whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in view.__doc__.splitlines()[1:] if line.lstrip()] | ||
|
||
if whitespace_counts: | ||
whitespace_pattern = '^' + (' ' * min(whitespace_counts)) | ||
return re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', view.__doc__) | ||
|
||
return view.__doc__ | ||
|
||
return '' |
Oops, something went wrong.