Skip to content

Commit c39925f

Browse files
committed
Added the tastypie lib to the repository
- Legacy-Id: 8742
1 parent 9883b8d commit c39925f

43 files changed

Lines changed: 7071 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

tastypie/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from __future__ import unicode_literals
2+
3+
4+
__author__ = 'Daniel Lindsley & the Tastypie core team'
5+
__version__ = (0, 12, 1)

tastypie/admin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from __future__ import unicode_literals
2+
from django.conf import settings
3+
from django.contrib import admin
4+
5+
6+
if 'django.contrib.auth' in settings.INSTALLED_APPS:
7+
from tastypie.models import ApiKey
8+
9+
class ApiKeyInline(admin.StackedInline):
10+
model = ApiKey
11+
extra = 0
12+
13+
ABSTRACT_APIKEY = getattr(settings, 'TASTYPIE_ABSTRACT_APIKEY', False)
14+
15+
if ABSTRACT_APIKEY and not isinstance(ABSTRACT_APIKEY, bool):
16+
raise TypeError("'TASTYPIE_ABSTRACT_APIKEY' must be either 'True' "
17+
"or 'False'.")
18+
19+
if not ABSTRACT_APIKEY:
20+
admin.site.register(ApiKey)

tastypie/api.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
from __future__ import unicode_literals
2+
import warnings
3+
from django.conf.urls import url, patterns, include
4+
from django.core.exceptions import ImproperlyConfigured
5+
from django.core.urlresolvers import reverse
6+
from django.http import HttpResponse, HttpResponseBadRequest
7+
from tastypie.exceptions import NotRegistered, BadRequest
8+
from tastypie.serializers import Serializer
9+
from tastypie.utils import trailing_slash, is_valid_jsonp_callback_value
10+
from tastypie.utils.mime import determine_format, build_content_type
11+
12+
class Api(object):
13+
"""
14+
Implements a registry to tie together the various resources that make up
15+
an API.
16+
17+
Especially useful for navigation, HATEOAS and for providing multiple
18+
versions of your API.
19+
20+
Optionally supplying ``api_name`` allows you to name the API. Generally,
21+
this is done with version numbers (i.e. ``v1``, ``v2``, etc.) but can
22+
be named any string.
23+
"""
24+
def __init__(self, api_name="v1", serializer_class=Serializer):
25+
self.api_name = api_name
26+
self._registry = {}
27+
self._canonicals = {}
28+
self.serializer = serializer_class()
29+
30+
def register(self, resource, canonical=True):
31+
"""
32+
Registers an instance of a ``Resource`` subclass with the API.
33+
34+
Optionally accept a ``canonical`` argument, which indicates that the
35+
resource being registered is the canonical variant. Defaults to
36+
``True``.
37+
"""
38+
resource_name = getattr(resource._meta, 'resource_name', None)
39+
40+
if resource_name is None:
41+
raise ImproperlyConfigured("Resource %r must define a 'resource_name'." % resource)
42+
43+
self._registry[resource_name] = resource
44+
45+
if canonical is True:
46+
if resource_name in self._canonicals:
47+
warnings.warn("A new resource '%r' is replacing the existing canonical URL for '%s'." % (resource, resource_name), Warning, stacklevel=2)
48+
49+
self._canonicals[resource_name] = resource
50+
# TODO: This is messy, but makes URI resolution on FK/M2M fields
51+
# work consistently.
52+
resource._meta.api_name = self.api_name
53+
resource.__class__.Meta.api_name = self.api_name
54+
55+
def unregister(self, resource_name):
56+
"""
57+
If present, unregisters a resource from the API.
58+
"""
59+
if resource_name in self._registry:
60+
del(self._registry[resource_name])
61+
62+
if resource_name in self._canonicals:
63+
del(self._canonicals[resource_name])
64+
65+
def canonical_resource_for(self, resource_name):
66+
"""
67+
Returns the canonical resource for a given ``resource_name``.
68+
"""
69+
if resource_name in self._canonicals:
70+
return self._canonicals[resource_name]
71+
72+
raise NotRegistered("No resource was registered as canonical for '%s'." % resource_name)
73+
74+
def wrap_view(self, view):
75+
def wrapper(request, *args, **kwargs):
76+
try:
77+
return getattr(self, view)(request, *args, **kwargs)
78+
except BadRequest:
79+
return HttpResponseBadRequest()
80+
return wrapper
81+
82+
def override_urls(self):
83+
"""
84+
Deprecated. Will be removed by v1.0.0. Please use ``prepend_urls`` instead.
85+
"""
86+
return []
87+
88+
def prepend_urls(self):
89+
"""
90+
A hook for adding your own URLs or matching before the default URLs.
91+
"""
92+
return []
93+
94+
@property
95+
def urls(self):
96+
"""
97+
Provides URLconf details for the ``Api`` and all registered
98+
``Resources`` beneath it.
99+
"""
100+
pattern_list = [
101+
url(r"^(?P<api_name>%s)%s$" % (self.api_name, trailing_slash()), self.wrap_view('top_level'), name="api_%s_top_level" % self.api_name),
102+
]
103+
104+
for name in sorted(self._registry.keys()):
105+
self._registry[name].api_name = self.api_name
106+
pattern_list.append((r"^(?P<api_name>%s)/" % self.api_name, include(self._registry[name].urls)))
107+
108+
urlpatterns = self.prepend_urls()
109+
110+
overridden_urls = self.override_urls()
111+
if overridden_urls:
112+
warnings.warn("'override_urls' is a deprecated method & will be removed by v1.0.0. Please rename your method to ``prepend_urls``.")
113+
urlpatterns += overridden_urls
114+
115+
urlpatterns += patterns('',
116+
*pattern_list
117+
)
118+
return urlpatterns
119+
120+
def top_level(self, request, api_name=None):
121+
"""
122+
A view that returns a serialized list of all resources registers
123+
to the ``Api``. Useful for discovery.
124+
"""
125+
available_resources = {}
126+
127+
if api_name is None:
128+
api_name = self.api_name
129+
130+
for name in sorted(self._registry.keys()):
131+
available_resources[name] = {
132+
'list_endpoint': self._build_reverse_url("api_dispatch_list", kwargs={
133+
'api_name': api_name,
134+
'resource_name': name,
135+
}),
136+
'schema': self._build_reverse_url("api_get_schema", kwargs={
137+
'api_name': api_name,
138+
'resource_name': name,
139+
}),
140+
}
141+
142+
desired_format = determine_format(request, self.serializer)
143+
144+
options = {}
145+
146+
if 'text/javascript' in desired_format:
147+
callback = request.GET.get('callback', 'callback')
148+
149+
if not is_valid_jsonp_callback_value(callback):
150+
raise BadRequest('JSONP callback name is invalid.')
151+
152+
options['callback'] = callback
153+
154+
serialized = self.serializer.serialize(available_resources, desired_format, options)
155+
return HttpResponse(content=serialized, content_type=build_content_type(desired_format))
156+
157+
def _build_reverse_url(self, name, args=None, kwargs=None):
158+
"""
159+
A convenience hook for overriding how URLs are built.
160+
161+
See ``NamespacedApi._build_reverse_url`` for an example.
162+
"""
163+
return reverse(name, args=args, kwargs=kwargs)
164+
165+
166+
class NamespacedApi(Api):
167+
"""
168+
An API subclass that respects Django namespaces.
169+
"""
170+
def __init__(self, api_name="v1", urlconf_namespace=None, **kwargs):
171+
super(NamespacedApi, self).__init__(api_name=api_name, **kwargs)
172+
self.urlconf_namespace = urlconf_namespace
173+
174+
def register(self, resource, canonical=True):
175+
super(NamespacedApi, self).register(resource, canonical=canonical)
176+
177+
if canonical is True:
178+
# Plop in the namespace here as well.
179+
resource._meta.urlconf_namespace = self.urlconf_namespace
180+
181+
def _build_reverse_url(self, name, args=None, kwargs=None):
182+
namespaced = "%s:%s" % (self.urlconf_namespace, name)
183+
return reverse(namespaced, args=args, kwargs=kwargs)

0 commit comments

Comments
 (0)