initial work on autodiscover

This commit is contained in:
Craig Blaszczyk 2011-08-29 02:33:58 +01:00
parent b71a24437a
commit 9f6d104dad
5 changed files with 227 additions and 1 deletions

View File

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

View 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

View File

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

View 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},
)

View File

@ -25,4 +25,10 @@ class UserGroupMap(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
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)