mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-28 04:24:00 +03:00
initial work on autodiscover
This commit is contained in:
parent
b71a24437a
commit
9f6d104dad
|
@ -1,3 +1,33 @@
|
||||||
__version__ = '0.2.3'
|
__version__ = '0.2.3'
|
||||||
|
|
||||||
VERSION = __version__ # synonym
|
VERSION = __version__ # synonym
|
||||||
|
|
||||||
|
from djangorestframework.builtins import DjangoRestFrameworkApi
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
|
import imp
|
||||||
|
|
||||||
|
__all__ = ('autodiscover','api', '__version__', 'VERSION')
|
||||||
|
|
||||||
|
api = DjangoRestFrameworkApi()
|
||||||
|
|
||||||
|
def autodiscover():
|
||||||
|
"""
|
||||||
|
Auto-discover INSTALLED_APPS api.py modules and fail silently when
|
||||||
|
not present. This forces an import on them to register any api entries they
|
||||||
|
may want.
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
|
for app in settings.INSTALLED_APPS:
|
||||||
|
# Attempt to import the app's gargoyle module.
|
||||||
|
before_import_registry = copy.copy(api._registry)
|
||||||
|
try:
|
||||||
|
import_module('%s.api' % app)
|
||||||
|
except:
|
||||||
|
# Reset the model registry to the state before the last import as
|
||||||
|
# this import will have to reoccur on the next request and this
|
||||||
|
# could raise NotRegistered and AlreadyRegistered exceptions
|
||||||
|
api._registry = before_import_registry
|
||||||
|
|
95
djangorestframework/builtins.py
Normal file
95
djangorestframework/builtins.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from django.conf.urls.defaults import patterns, url, include
|
||||||
|
|
||||||
|
class ApiEntry(object):
|
||||||
|
"""
|
||||||
|
Hold information about a Resource in the api
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, resource, view, name, namespace=None):
|
||||||
|
self.resource, self.view, self.name = resource, view, name
|
||||||
|
self.namespace = namespace is not None and namespace or ''
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
"""
|
||||||
|
Create the URLs corresponding to this view.
|
||||||
|
"""
|
||||||
|
from djangorestframework.mixins import ListModelMixin, InstanceMixin
|
||||||
|
if self.namespace == '':
|
||||||
|
namespaced_name = self.name
|
||||||
|
else:
|
||||||
|
namespaced_name = '%s/%s' % (self.namespace, self.name)
|
||||||
|
|
||||||
|
if issubclass(self.view, ListModelMixin):
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^%s/$' % (namespaced_name),
|
||||||
|
self.view.as_view(resource=self.resource),
|
||||||
|
name=self.name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif issubclass(self.view, InstanceMixin):
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^%s/(?P<pk>[0-9a-zA-Z]+)/$' % (namespaced_name),
|
||||||
|
self.view.as_view(resource=self.resource),
|
||||||
|
name=self.name + '_change',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return urlpatterns
|
||||||
|
|
||||||
|
|
||||||
|
def urls(self):
|
||||||
|
return self.get_urls(), 'api', self.namespace
|
||||||
|
urls = property(urls)
|
||||||
|
|
||||||
|
class DjangoRestFrameworkApi(object):
|
||||||
|
app_name = 'api'
|
||||||
|
namespace = 'api'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._registry = {}
|
||||||
|
super(DjangoRestFrameworkApi, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def register(self, view, resource, namespace=None, name=None):
|
||||||
|
"""
|
||||||
|
Register a resource and a view into the API, optionally giving an
|
||||||
|
override for the resource's name and a namespace for the URLs.
|
||||||
|
"""
|
||||||
|
if name is None:
|
||||||
|
if hasattr(resource, 'model'):
|
||||||
|
# Use the model's name as the resource_name
|
||||||
|
name = resource.model.__name__.lower()
|
||||||
|
else:
|
||||||
|
# Use the Resource's name as the resource_name
|
||||||
|
name = resource.__name__.lower()
|
||||||
|
|
||||||
|
resource.api_name = name
|
||||||
|
|
||||||
|
if namespace not in self._registry:
|
||||||
|
self._registry[namespace] = {}
|
||||||
|
|
||||||
|
if name not in self._registry[namespace]:
|
||||||
|
self._registry[namespace][name] = []
|
||||||
|
|
||||||
|
api_entry = ApiEntry(resource, view, name, namespace)
|
||||||
|
self._registry[namespace][name].append(api_entry)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def urls(self):
|
||||||
|
return self.get_urls(), self.app_name, self.namespace
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
"""
|
||||||
|
Return all of the urls for this API
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Site-wide views.
|
||||||
|
urlpatterns = patterns('',)
|
||||||
|
|
||||||
|
# Add in each resource's views.
|
||||||
|
for namespace in self._registry.keys():
|
||||||
|
for resource_name in self._registry[namespace].keys():
|
||||||
|
for api_entry in self._registry[namespace][resource_name]:
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
url(r'^', include(api_entry.urls))
|
||||||
|
)
|
||||||
|
|
||||||
|
return urlpatterns
|
|
@ -379,6 +379,16 @@ class ModelResource(FormResource):
|
||||||
return reverse(self.view_callable[0], kwargs=instance_attrs)
|
return reverse(self.view_callable[0], kwargs=instance_attrs)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if hasattr(self, 'api_name'):
|
||||||
|
# Get the URL from the API
|
||||||
|
try:
|
||||||
|
return reverse(
|
||||||
|
'%s:%s_change' % ('api', self.api_name), args=(instance.pk,)
|
||||||
|
)
|
||||||
|
except NoReverseMatch:
|
||||||
|
pass
|
||||||
|
|
||||||
raise _SkipField
|
raise _SkipField
|
||||||
|
|
||||||
|
|
||||||
|
|
85
djangorestframework/tests/api.py
Normal file
85
djangorestframework/tests/api.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
from django.test.testcases import TestCase
|
||||||
|
from djangorestframework.builtins import DjangoRestFrameworkApi
|
||||||
|
from djangorestframework.resources import Resource, ModelResource
|
||||||
|
from djangorestframework.tests.models import Company, Employee
|
||||||
|
from django.conf.urls.defaults import patterns, url, include
|
||||||
|
from djangorestframework.views import ListOrCreateModelView, InstanceModelView,\
|
||||||
|
ListModelView
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
__all__ = ('ApiTestCase',)
|
||||||
|
|
||||||
|
class CompanyResource(ModelResource):
|
||||||
|
model = Company
|
||||||
|
|
||||||
|
class EmployeeResource(ModelResource):
|
||||||
|
model = Employee
|
||||||
|
|
||||||
|
class UrlConfModule(object):
|
||||||
|
|
||||||
|
def __init__(self, api):
|
||||||
|
self.api = api
|
||||||
|
|
||||||
|
def _get_urlpatterns(self):
|
||||||
|
return patterns('',
|
||||||
|
url(r'^', include(self.api.urls)),
|
||||||
|
)
|
||||||
|
|
||||||
|
urlpatterns = property(_get_urlpatterns)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.api = DjangoRestFrameworkApi()
|
||||||
|
self.urlconfmodule = UrlConfModule(self.api)
|
||||||
|
|
||||||
|
def test_list_view(self):
|
||||||
|
# Check that the URL gets registered
|
||||||
|
self.api.register(ListModelView, CompanyResource)
|
||||||
|
reverse('api:company', urlconf=self.urlconfmodule)
|
||||||
|
|
||||||
|
def test_instance_view(self):
|
||||||
|
self.api.register(InstanceModelView, CompanyResource)
|
||||||
|
company = Company(name='Acme Ltd')
|
||||||
|
company.save()
|
||||||
|
# Check that the URL gets registered
|
||||||
|
reverse(
|
||||||
|
'api:company_change', urlconf=self.urlconfmodule,
|
||||||
|
kwargs={'pk':company.id},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_instance_view_with_nonumeric_primary_key(self):
|
||||||
|
"""
|
||||||
|
Check that the api can properly reverse urls for models with
|
||||||
|
non-numeric primary keys
|
||||||
|
"""
|
||||||
|
self.api.register(InstanceModelView, EmployeeResource)
|
||||||
|
employee = Employee(employee_id='EMP001')
|
||||||
|
employee.save()
|
||||||
|
reverse(
|
||||||
|
'api:employee_change', urlconf=self.urlconfmodule,
|
||||||
|
kwargs={'pk':employee.employee_id}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_with_different_name(self):
|
||||||
|
self.api.register(InstanceModelView, CompanyResource, name='abcdef')
|
||||||
|
company = Company(name='Acme Ltd')
|
||||||
|
company.save()
|
||||||
|
# Check that the URL gets registered
|
||||||
|
reverse(
|
||||||
|
'api:abcdef_change', urlconf=self.urlconfmodule,
|
||||||
|
kwargs={'pk':company.id},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_with_prefix(self):
|
||||||
|
self.api.register(
|
||||||
|
InstanceModelView, CompanyResource, namespace='abcdef'
|
||||||
|
)
|
||||||
|
company = Company(name='Acme Ltd')
|
||||||
|
company.save()
|
||||||
|
# Check that the URL gets registered
|
||||||
|
reverse(
|
||||||
|
'api:abcdef:company_change', urlconf=self.urlconfmodule,
|
||||||
|
kwargs={'pk':company.id},
|
||||||
|
)
|
|
@ -26,3 +26,9 @@ class UserGroupMap(models.Model):
|
||||||
return ('user_group_map', (), {
|
return ('user_group_map', (), {
|
||||||
'pk': self.id
|
'pk': self.id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class Company(models.Model):
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class Employee(models.Model):
|
||||||
|
employee_id = models.CharField(max_length=20, primary_key=True)
|
Loading…
Reference in New Issue
Block a user