mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-30 21:44:04 +03:00
initial work on autodiscover
This commit is contained in:
parent
b71a24437a
commit
9f6d104dad
|
@ -1,3 +1,33 @@
|
|||
__version__ = '0.2.3'
|
||||
|
||||
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)
|
||||
except NoReverseMatch:
|
||||
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
|
||||
|
||||
|
||||
|
|
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', (), {
|
||||
'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