mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-09 08:00:52 +03:00
Remove examples (to be moved to a seperate project)
This commit is contained in:
parent
ecd3733c5e
commit
eea2aa0437
|
@ -1 +0,0 @@
|
||||||
rest
|
|
|
@ -1,41 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
from django.template.defaultfilters import slugify
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
def uuid_str():
|
|
||||||
return str(uuid.uuid1())
|
|
||||||
|
|
||||||
|
|
||||||
RATING_CHOICES = ((0, 'Awful'),
|
|
||||||
(1, 'Poor'),
|
|
||||||
(2, 'OK'),
|
|
||||||
(3, 'Good'),
|
|
||||||
(4, 'Excellent'))
|
|
||||||
|
|
||||||
MAX_POSTS = 10
|
|
||||||
|
|
||||||
|
|
||||||
class BlogPost(models.Model):
|
|
||||||
key = models.CharField(primary_key=True, max_length=64, default=uuid_str, editable=False)
|
|
||||||
title = models.CharField(max_length=128)
|
|
||||||
content = models.TextField()
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
|
||||||
slug = models.SlugField(editable=False, default='')
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
For the purposes of the sandbox, limit the maximum number of stored models.
|
|
||||||
"""
|
|
||||||
self.slug = slugify(self.title)
|
|
||||||
super(self.__class__, self).save(*args, **kwargs)
|
|
||||||
for obj in self.__class__.objects.order_by('-created')[MAX_POSTS:]:
|
|
||||||
obj.delete()
|
|
||||||
|
|
||||||
|
|
||||||
class Comment(models.Model):
|
|
||||||
blogpost = models.ForeignKey(BlogPost, editable=False, related_name='comments')
|
|
||||||
username = models.CharField(max_length=128)
|
|
||||||
comment = models.TextField()
|
|
||||||
rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES, help_text='How did you rate this post?')
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
|
|
@ -1,36 +0,0 @@
|
||||||
from djangorestframework.resources import ModelResource
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from blogpost.models import BlogPost, Comment
|
|
||||||
|
|
||||||
|
|
||||||
class BlogPostResource(ModelResource):
|
|
||||||
"""
|
|
||||||
A Blog Post has a *title* and *content*, and can be associated with zero or more comments.
|
|
||||||
"""
|
|
||||||
model = BlogPost
|
|
||||||
fields = ('created', 'title', 'slug', 'content', 'url', 'comments')
|
|
||||||
ordering = ('-created',)
|
|
||||||
|
|
||||||
def url(self, instance):
|
|
||||||
return reverse('blog-post',
|
|
||||||
kwargs={'key': instance.key},
|
|
||||||
request=self.request)
|
|
||||||
|
|
||||||
def comments(self, instance):
|
|
||||||
return reverse('comments',
|
|
||||||
kwargs={'blogpost': instance.key},
|
|
||||||
request=self.request)
|
|
||||||
|
|
||||||
|
|
||||||
class CommentResource(ModelResource):
|
|
||||||
"""
|
|
||||||
A Comment is associated with a given Blog Post and has a *username* and *comment*, and optionally a *rating*.
|
|
||||||
"""
|
|
||||||
model = Comment
|
|
||||||
fields = ('username', 'comment', 'created', 'rating', 'url', 'blogpost')
|
|
||||||
ordering = ('-created',)
|
|
||||||
|
|
||||||
def blogpost(self, instance):
|
|
||||||
return reverse('blog-post',
|
|
||||||
kwargs={'key': instance.blogpost.key},
|
|
||||||
request=self.request)
|
|
|
@ -1,210 +0,0 @@
|
||||||
"""Test a range of REST API usage of the example application.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.utils import simplejson as json
|
|
||||||
|
|
||||||
from djangorestframework.compat import RequestFactory
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from djangorestframework.views import InstanceModelView, ListOrCreateModelView
|
|
||||||
|
|
||||||
from blogpost import models, urls
|
|
||||||
#import blogpost
|
|
||||||
|
|
||||||
|
|
||||||
# class AcceptHeaderTests(TestCase):
|
|
||||||
# """Test correct behaviour of the Accept header as specified by RFC 2616:
|
|
||||||
#
|
|
||||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"""
|
|
||||||
#
|
|
||||||
# def assert_accept_mimetype(self, mimetype, expect=None):
|
|
||||||
# """Assert that a request with given mimetype in the accept header,
|
|
||||||
# gives a response with the appropriate content-type."""
|
|
||||||
# if expect is None:
|
|
||||||
# expect = mimetype
|
|
||||||
#
|
|
||||||
# resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT=mimetype)
|
|
||||||
#
|
|
||||||
# self.assertEquals(resp['content-type'], expect)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def dont_test_accept_json(self):
|
|
||||||
# """Ensure server responds with Content-Type of JSON when requested."""
|
|
||||||
# self.assert_accept_mimetype('application/json')
|
|
||||||
#
|
|
||||||
# def dont_test_accept_xml(self):
|
|
||||||
# """Ensure server responds with Content-Type of XML when requested."""
|
|
||||||
# self.assert_accept_mimetype('application/xml')
|
|
||||||
#
|
|
||||||
# def dont_test_accept_json_when_prefered_to_xml(self):
|
|
||||||
# """Ensure server responds with Content-Type of JSON when it is the client's prefered choice."""
|
|
||||||
# self.assert_accept_mimetype('application/json;q=0.9, application/xml;q=0.1', expect='application/json')
|
|
||||||
#
|
|
||||||
# def dont_test_accept_xml_when_prefered_to_json(self):
|
|
||||||
# """Ensure server responds with Content-Type of XML when it is the client's prefered choice."""
|
|
||||||
# self.assert_accept_mimetype('application/json;q=0.1, application/xml;q=0.9', expect='application/xml')
|
|
||||||
#
|
|
||||||
# def dont_test_default_json_prefered(self):
|
|
||||||
# """Ensure server responds with JSON in preference to XML."""
|
|
||||||
# self.assert_accept_mimetype('application/json,application/xml', expect='application/json')
|
|
||||||
#
|
|
||||||
# def dont_test_accept_generic_subtype_format(self):
|
|
||||||
# """Ensure server responds with an appropriate type, when the subtype is left generic."""
|
|
||||||
# self.assert_accept_mimetype('text/*', expect='text/html')
|
|
||||||
#
|
|
||||||
# def dont_test_accept_generic_type_format(self):
|
|
||||||
# """Ensure server responds with an appropriate type, when the type and subtype are left generic."""
|
|
||||||
# self.assert_accept_mimetype('*/*', expect='application/json')
|
|
||||||
#
|
|
||||||
# def dont_test_invalid_accept_header_returns_406(self):
|
|
||||||
# """Ensure server returns a 406 (not acceptable) response if we set the Accept header to junk."""
|
|
||||||
# resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT='invalid/invalid')
|
|
||||||
# self.assertNotEquals(resp['content-type'], 'invalid/invalid')
|
|
||||||
# self.assertEquals(resp.status_code, 406)
|
|
||||||
#
|
|
||||||
# def dont_test_prefer_specific_over_generic(self): # This test is broken right now
|
|
||||||
# """More specific accept types have precedence over less specific types."""
|
|
||||||
# self.assert_accept_mimetype('application/xml, */*', expect='application/xml')
|
|
||||||
# self.assert_accept_mimetype('*/*, application/xml', expect='application/xml')
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# class AllowedMethodsTests(TestCase):
|
|
||||||
# """Basic tests to check that only allowed operations may be performed on a Resource"""
|
|
||||||
#
|
|
||||||
# def dont_test_reading_a_read_only_resource_is_allowed(self):
|
|
||||||
# """GET requests on a read only resource should default to a 200 (OK) response"""
|
|
||||||
# resp = self.client.get(reverse(views.RootResource))
|
|
||||||
# self.assertEquals(resp.status_code, 200)
|
|
||||||
#
|
|
||||||
# def dont_test_writing_to_read_only_resource_is_not_allowed(self):
|
|
||||||
# """PUT requests on a read only resource should default to a 405 (method not allowed) response"""
|
|
||||||
# resp = self.client.put(reverse(views.RootResource), {})
|
|
||||||
# self.assertEquals(resp.status_code, 405)
|
|
||||||
#
|
|
||||||
# def test_reading_write_only_not_allowed(self):
|
|
||||||
# resp = self.client.get(reverse(views.WriteOnlyResource))
|
|
||||||
# self.assertEquals(resp.status_code, 405)
|
|
||||||
#
|
|
||||||
# def test_writing_write_only_allowed(self):
|
|
||||||
# resp = self.client.put(reverse(views.WriteOnlyResource), {})
|
|
||||||
# self.assertEquals(resp.status_code, 200)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#class EncodeDecodeTests(TestCase):
|
|
||||||
# def setUp(self):
|
|
||||||
# super(self.__class__, self).setUp()
|
|
||||||
# self.input = {'a': 1, 'b': 'example'}
|
|
||||||
#
|
|
||||||
# def test_encode_form_decode_json(self):
|
|
||||||
# content = self.input
|
|
||||||
# resp = self.client.put(reverse(views.WriteOnlyResource), content)
|
|
||||||
# output = json.loads(resp.content)
|
|
||||||
# self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
# def test_encode_json_decode_json(self):
|
|
||||||
# content = json.dumps(self.input)
|
|
||||||
# resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json')
|
|
||||||
# output = json.loads(resp.content)
|
|
||||||
# self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
# #def test_encode_xml_decode_json(self):
|
|
||||||
# # content = dict2xml(self.input)
|
|
||||||
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/json')
|
|
||||||
# # output = json.loads(resp.content)
|
|
||||||
# # self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
# #def test_encode_form_decode_xml(self):
|
|
||||||
# # content = self.input
|
|
||||||
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, HTTP_ACCEPT='application/xml')
|
|
||||||
# # output = xml2dict(resp.content)
|
|
||||||
# # self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
# #def test_encode_json_decode_xml(self):
|
|
||||||
# # content = json.dumps(self.input)
|
|
||||||
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
|
|
||||||
# # output = xml2dict(resp.content)
|
|
||||||
# # self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
# #def test_encode_xml_decode_xml(self):
|
|
||||||
# # content = dict2xml(self.input)
|
|
||||||
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
|
|
||||||
# # output = xml2dict(resp.content)
|
|
||||||
# # self.assertEquals(self.input, output)
|
|
||||||
#
|
|
||||||
#class ModelTests(TestCase):
|
|
||||||
# def test_create_container(self):
|
|
||||||
# content = json.dumps({'name': 'example'})
|
|
||||||
# resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json')
|
|
||||||
# output = json.loads(resp.content)
|
|
||||||
# self.assertEquals(resp.status_code, 201)
|
|
||||||
# self.assertEquals(output['name'], 'example')
|
|
||||||
# self.assertEquals(set(output.keys()), set(('absolute_uri', 'name', 'key')))
|
|
||||||
#
|
|
||||||
#class CreatedModelTests(TestCase):
|
|
||||||
# def setUp(self):
|
|
||||||
# content = json.dumps({'name': 'example'})
|
|
||||||
# resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json', HTTP_ACCEPT='application/json')
|
|
||||||
# self.container = json.loads(resp.content)
|
|
||||||
#
|
|
||||||
# def test_read_container(self):
|
|
||||||
# resp = self.client.get(self.container["absolute_uri"])
|
|
||||||
# self.assertEquals(resp.status_code, 200)
|
|
||||||
# container = json.loads(resp.content)
|
|
||||||
# self.assertEquals(container, self.container)
|
|
||||||
#
|
|
||||||
# def test_delete_container(self):
|
|
||||||
# resp = self.client.delete(self.container["absolute_uri"])
|
|
||||||
# self.assertEquals(resp.status_code, 204)
|
|
||||||
# self.assertEquals(resp.content, '')
|
|
||||||
#
|
|
||||||
# def test_update_container(self):
|
|
||||||
# self.container['name'] = 'new'
|
|
||||||
# content = json.dumps(self.container)
|
|
||||||
# resp = self.client.put(self.container["absolute_uri"], content, 'application/json')
|
|
||||||
# self.assertEquals(resp.status_code, 200)
|
|
||||||
# container = json.loads(resp.content)
|
|
||||||
# self.assertEquals(container, self.container)
|
|
||||||
|
|
||||||
|
|
||||||
#above testcases need to probably moved to the core
|
|
||||||
|
|
||||||
|
|
||||||
class TestRotation(TestCase):
|
|
||||||
"""For the example the maximum amount of Blogposts is capped off at views.MAX_POSTS.
|
|
||||||
Whenever a new Blogpost is posted the oldest one should be popped."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.factory = RequestFactory()
|
|
||||||
models.BlogPost.objects.all().delete()
|
|
||||||
|
|
||||||
def test_get_to_root(self):
|
|
||||||
'''Simple get to the *root* url of blogposts'''
|
|
||||||
request = self.factory.get('/blog-post')
|
|
||||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
|
||||||
response = view(request)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_blogposts_not_exceed_MAX_POSTS(self):
|
|
||||||
'''Posting blog-posts should not result in more than MAX_POSTS items stored.'''
|
|
||||||
for post in range(models.MAX_POSTS + 5):
|
|
||||||
form_data = {'title': 'This is post #%s' % post, 'content': 'This is the content of post #%s' % post}
|
|
||||||
request = self.factory.post('/blog-post', data=form_data)
|
|
||||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
|
||||||
view(request)
|
|
||||||
self.assertEquals(len(models.BlogPost.objects.all()),models.MAX_POSTS)
|
|
||||||
|
|
||||||
def test_fifo_behaviour(self):
|
|
||||||
'''It's fine that the Blogposts are capped off at MAX_POSTS. But we want to make sure we see FIFO behaviour.'''
|
|
||||||
for post in range(15):
|
|
||||||
form_data = {'title': '%s' % post, 'content': 'This is the content of post #%s' % post}
|
|
||||||
request = self.factory.post('/blog-post', data=form_data)
|
|
||||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
|
||||||
view(request)
|
|
||||||
request = self.factory.get('/blog-post')
|
|
||||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
|
||||||
response = view(request)
|
|
||||||
response_posts = json.loads(response.content)
|
|
||||||
response_titles = [d['title'] for d in response_posts]
|
|
||||||
response_titles.reverse()
|
|
||||||
self.assertEquals(response_titles, ['%s' % i for i in range(models.MAX_POSTS - 5, models.MAX_POSTS + 5)])
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from djangorestframework.views import ListOrCreateModelView, InstanceModelView
|
|
||||||
from blogpost.resources import BlogPostResource, CommentResource
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', ListOrCreateModelView.as_view(resource=BlogPostResource), name='blog-posts-root'),
|
|
||||||
url(r'^(?P<key>[^/]+)/$', InstanceModelView.as_view(resource=BlogPostResource), name='blog-post'),
|
|
||||||
url(r'^(?P<blogpost>[^/]+)/comments/$', ListOrCreateModelView.as_view(resource=CommentResource), name='comments'),
|
|
||||||
url(r'^(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$', InstanceModelView.as_view(resource=CommentResource)),
|
|
||||||
)
|
|
|
@ -1,62 +0,0 @@
|
||||||
# This is an example epio.ini file.
|
|
||||||
# We suggest you edit it to fit your application's needs.
|
|
||||||
# Documentation for the options is available at www.ep.io/docs/epioini/
|
|
||||||
|
|
||||||
[wsgi]
|
|
||||||
|
|
||||||
# Location of your requirements file
|
|
||||||
requirements = requirements-epio.txt
|
|
||||||
|
|
||||||
|
|
||||||
[static]
|
|
||||||
|
|
||||||
# Serve the static directory directly as /static
|
|
||||||
/static/admin = ../shortcuts/django-admin-media/
|
|
||||||
|
|
||||||
|
|
||||||
[services]
|
|
||||||
|
|
||||||
# Uncomment to enable the PostgreSQL service.
|
|
||||||
postgres = true
|
|
||||||
|
|
||||||
# Uncomment to enable the Redis service
|
|
||||||
# redis = true
|
|
||||||
|
|
||||||
|
|
||||||
[checkout]
|
|
||||||
|
|
||||||
# By default your code is put in a directory called 'app'.
|
|
||||||
# You can change that here.
|
|
||||||
# directory_name = my_project
|
|
||||||
|
|
||||||
|
|
||||||
[env]
|
|
||||||
|
|
||||||
# Set any additional environment variables here. For example:
|
|
||||||
# IN_PRODUCTION = true
|
|
||||||
|
|
||||||
|
|
||||||
[symlinks]
|
|
||||||
|
|
||||||
# Any symlinks you'd like to add. As an example, link the symlink 'config.py'
|
|
||||||
# to the real file 'configs/epio.py':
|
|
||||||
# config.py = configs/epio.py
|
|
||||||
|
|
||||||
media/ = %(data_directory)s/
|
|
||||||
|
|
||||||
# #### If you're using Django, you'll want to uncomment some or all of these lines ####
|
|
||||||
# [django]
|
|
||||||
# # Path to your project root, relative to this directory.
|
|
||||||
# base = .
|
|
||||||
#
|
|
||||||
# [static]
|
|
||||||
# Serve the admin media
|
|
||||||
# # Django 1.3
|
|
||||||
# /static/admin = ../shortcuts/django-admin-media/
|
|
||||||
# # Django 1.2 and below
|
|
||||||
# /media = ../shortcuts/django-admin-media/
|
|
||||||
#
|
|
||||||
# [env]
|
|
||||||
# # Use a different settings module for ep.io (i.e. with DEBUG=False)
|
|
||||||
# DJANGO_SETTINGS_MODULE = production_settings
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
from django.core.management import execute_manager
|
|
||||||
try:
|
|
||||||
import settings # Assumed to be in the same directory.
|
|
||||||
except ImportError:
|
|
||||||
import sys
|
|
||||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
execute_manager(settings)
|
|
|
@ -1 +0,0 @@
|
||||||
Force media/objectstore directory to created
|
|
|
@ -1 +0,0 @@
|
||||||
Force media/pygments directory to created
|
|
|
@ -1,25 +0,0 @@
|
||||||
from djangorestframework.compat import View # Use Django 1.3's django.views.generic.View, or fall back to a clone of that if Django < 1.3
|
|
||||||
from djangorestframework.mixins import ResponseMixin
|
|
||||||
from djangorestframework.renderers import DEFAULT_RENDERERS
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
|
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleView(ResponseMixin, View):
|
|
||||||
"""An example view using Django 1.3's class based views.
|
|
||||||
Uses djangorestframework's RendererMixin to provide support for multiple
|
|
||||||
output formats."""
|
|
||||||
renderers = DEFAULT_RENDERERS
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
url = reverse('mixin-view', request=request)
|
|
||||||
response = Response(200, {'description': 'Some example content',
|
|
||||||
'url': url})
|
|
||||||
return self.render(response)
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', ExampleView.as_view(), name='mixin-view'),
|
|
||||||
)
|
|
|
@ -1,18 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
MAX_INSTANCES = 10
|
|
||||||
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
|
||||||
foo = models.BooleanField()
|
|
||||||
bar = models.IntegerField(help_text='Must be an integer.')
|
|
||||||
baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
For the purposes of the sandbox limit the maximum number of stored models.
|
|
||||||
"""
|
|
||||||
super(MyModel, self).save(*args, **kwargs)
|
|
||||||
while MyModel.objects.all().count() > MAX_INSTANCES:
|
|
||||||
MyModel.objects.all().order_by('-created')[0].delete()
|
|
|
@ -1,14 +0,0 @@
|
||||||
from djangorestframework.resources import ModelResource
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from modelresourceexample.models import MyModel
|
|
||||||
|
|
||||||
|
|
||||||
class MyModelResource(ModelResource):
|
|
||||||
model = MyModel
|
|
||||||
fields = ('foo', 'bar', 'baz', 'url')
|
|
||||||
ordering = ('created',)
|
|
||||||
|
|
||||||
def url(self, instance):
|
|
||||||
return reverse('model-resource-instance',
|
|
||||||
kwargs={'id': instance.id},
|
|
||||||
request=self.request)
|
|
|
@ -1,11 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from djangorestframework.views import ListOrCreateModelView, InstanceModelView
|
|
||||||
from modelresourceexample.resources import MyModelResource
|
|
||||||
|
|
||||||
my_model_list = ListOrCreateModelView.as_view(resource=MyModelResource)
|
|
||||||
my_model_instance = InstanceModelView.as_view(resource=MyModelResource)
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', my_model_list, name='model-resource-root'),
|
|
||||||
url(r'^(?P<id>[0-9]+)/$', my_model_instance, name='model-resource-instance'),
|
|
||||||
)
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from objectstore.views import ObjectStoreRoot, StoredObject
|
|
||||||
|
|
||||||
urlpatterns = patterns('objectstore.views',
|
|
||||||
url(r'^$', ObjectStoreRoot.as_view(), name='object-store-root'),
|
|
||||||
url(r'^(?P<key>[A-Za-z0-9_-]{1,64})/$', StoredObject.as_view(), name='stored-object'),
|
|
||||||
)
|
|
|
@ -1,109 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
from djangorestframework import status
|
|
||||||
|
|
||||||
import pickle
|
|
||||||
import os
|
|
||||||
import uuid
|
|
||||||
import operator
|
|
||||||
|
|
||||||
OBJECT_STORE_DIR = os.path.join(settings.MEDIA_ROOT, 'objectstore')
|
|
||||||
MAX_FILES = 10
|
|
||||||
|
|
||||||
if not os.path.exists(OBJECT_STORE_DIR):
|
|
||||||
os.makedirs(OBJECT_STORE_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_oldest_files(dir, max_files):
|
|
||||||
"""
|
|
||||||
Remove the oldest files in a directory 'dir', leaving at most 'max_files' remaining.
|
|
||||||
We use this to limit the number of resources in the sandbox.
|
|
||||||
"""
|
|
||||||
filepaths = [os.path.join(dir, file) for file in os.listdir(dir) if not file.startswith('.')]
|
|
||||||
ctime_sorted_paths = [item[0] for item in sorted([(path, os.path.getctime(path)) for path in filepaths],
|
|
||||||
key=operator.itemgetter(1), reverse=True)]
|
|
||||||
[os.remove(path) for path in ctime_sorted_paths[max_files:]]
|
|
||||||
|
|
||||||
|
|
||||||
def get_filename(key):
|
|
||||||
"""
|
|
||||||
Given a stored object's key returns the file's path.
|
|
||||||
"""
|
|
||||||
return os.path.join(OBJECT_STORE_DIR, key)
|
|
||||||
|
|
||||||
|
|
||||||
def get_file_url(key, request):
|
|
||||||
"""
|
|
||||||
Given a stored object's key returns the URL for the object.
|
|
||||||
"""
|
|
||||||
return reverse('stored-object', kwargs={'key': key}, request=request)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectStoreRoot(View):
|
|
||||||
"""
|
|
||||||
Root of the Object Store API.
|
|
||||||
Allows the client to get a complete list of all the stored objects, or to create a new stored object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
"""
|
|
||||||
Return a list of all the stored object URLs. (Ordered by creation time, newest first)
|
|
||||||
"""
|
|
||||||
filepaths = [os.path.join(OBJECT_STORE_DIR, file)
|
|
||||||
for file in os.listdir(OBJECT_STORE_DIR)
|
|
||||||
if not file.startswith('.')]
|
|
||||||
ctime_sorted_basenames = [item[0] for item in sorted([(os.path.basename(path), os.path.getctime(path)) for path in filepaths],
|
|
||||||
key=operator.itemgetter(1), reverse=True)]
|
|
||||||
content = [get_file_url(key, request)
|
|
||||||
for key in ctime_sorted_basenames]
|
|
||||||
return Response(content)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
"""
|
|
||||||
Create a new stored object, with a unique key.
|
|
||||||
"""
|
|
||||||
key = str(uuid.uuid1())
|
|
||||||
filename = get_filename(key)
|
|
||||||
pickle.dump(self.CONTENT, open(filename, 'wb'))
|
|
||||||
|
|
||||||
remove_oldest_files(OBJECT_STORE_DIR, MAX_FILES)
|
|
||||||
url = get_file_url(key, request)
|
|
||||||
return Response(self.CONTENT, status.HTTP_201_CREATED, {'Location': url})
|
|
||||||
|
|
||||||
|
|
||||||
class StoredObject(View):
|
|
||||||
"""
|
|
||||||
Represents a stored object.
|
|
||||||
The object may be any picklable content.
|
|
||||||
"""
|
|
||||||
def get(self, request, key):
|
|
||||||
"""
|
|
||||||
Return a stored object, by unpickling the contents of a locally
|
|
||||||
stored file.
|
|
||||||
"""
|
|
||||||
filename = get_filename(key)
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
return Response(pickle.load(open(filename, 'rb')))
|
|
||||||
|
|
||||||
def put(self, request, key):
|
|
||||||
"""
|
|
||||||
Update/create a stored object, by pickling the request content to a
|
|
||||||
locally stored file.
|
|
||||||
"""
|
|
||||||
filename = get_filename(key)
|
|
||||||
pickle.dump(self.CONTENT, open(filename, 'wb'))
|
|
||||||
return Response(self.CONTENT)
|
|
||||||
|
|
||||||
def delete(self, request, key):
|
|
||||||
"""
|
|
||||||
Delete a stored object, by removing it's pickled file.
|
|
||||||
"""
|
|
||||||
filename = get_filename(key)
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
os.remove(filename)
|
|
||||||
return Response()
|
|
|
@ -1,18 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"pk": 2,
|
|
||||||
"model": "auth.user",
|
|
||||||
"fields": {
|
|
||||||
"username": "test",
|
|
||||||
"first_name": "",
|
|
||||||
"last_name": "",
|
|
||||||
"is_active": true,
|
|
||||||
"is_superuser": false,
|
|
||||||
"is_staff": false,
|
|
||||||
"groups": [],
|
|
||||||
"user_permissions": [],
|
|
||||||
"password": "sha1$b3dff$671b4ab97f2714446da32670d27576614e176758",
|
|
||||||
"email": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1 +0,0 @@
|
||||||
#for fixture loading
|
|
|
@ -1,27 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.test.client import Client
|
|
||||||
|
|
||||||
|
|
||||||
class NaviguatePermissionsExamples(TestCase):
|
|
||||||
"""
|
|
||||||
Sanity checks for permissions examples
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_throttled_resource(self):
|
|
||||||
url = reverse('throttled-resource')
|
|
||||||
for i in range(0, 10):
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 503)
|
|
||||||
|
|
||||||
|
|
||||||
def test_loggedin_resource(self):
|
|
||||||
url = reverse('loggedin-resource')
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 403)
|
|
||||||
loggedin_client = Client()
|
|
||||||
loggedin_client.login(username='test', password='test')
|
|
||||||
response = loggedin_client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from permissionsexample.views import PermissionsExampleView, ThrottlingExampleView, LoggedInExampleView
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', PermissionsExampleView.as_view(), name='permissions-example'),
|
|
||||||
url(r'^throttling$', ThrottlingExampleView.as_view(), name='throttled-resource'),
|
|
||||||
url(r'^loggedin$', LoggedInExampleView.as_view(), name='loggedin-resource'),
|
|
||||||
)
|
|
|
@ -1,53 +0,0 @@
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
from djangorestframework.permissions import PerUserThrottling, IsAuthenticated
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
|
|
||||||
|
|
||||||
class PermissionsExampleView(View):
|
|
||||||
"""
|
|
||||||
A container view for permissions examples.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
return Response([
|
|
||||||
{
|
|
||||||
'name': 'Throttling Example',
|
|
||||||
'url': reverse('throttled-resource', request)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Logged in example',
|
|
||||||
'url': reverse('loggedin-resource', request)
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class ThrottlingExampleView(View):
|
|
||||||
"""
|
|
||||||
A basic read-only View that has a **per-user throttle** of 10 requests per minute.
|
|
||||||
|
|
||||||
If a user exceeds the 10 requests limit within a period of one minute, the
|
|
||||||
throttle will be applied until 60 seconds have passed since the first request.
|
|
||||||
"""
|
|
||||||
|
|
||||||
permissions_classes = (PerUserThrottling,)
|
|
||||||
throttle = '10/min'
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
"""
|
|
||||||
Handle GET requests.
|
|
||||||
"""
|
|
||||||
return Response("Successful response to GET request because throttle is not yet active.")
|
|
||||||
|
|
||||||
|
|
||||||
class LoggedInExampleView(View):
|
|
||||||
"""
|
|
||||||
You can login with **'test', 'test'.** or use curl:
|
|
||||||
|
|
||||||
`curl -X GET -H 'Accept: application/json' -u test:test http://localhost:8000/permissions-example`
|
|
||||||
"""
|
|
||||||
|
|
||||||
permissions_classes = (IsAuthenticated, )
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
return Response('You have permission to view this resource')
|
|
|
@ -1,27 +0,0 @@
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from pygments.lexers import get_all_lexers
|
|
||||||
from pygments.styles import get_all_styles
|
|
||||||
|
|
||||||
LEXER_CHOICES = sorted([(item[1][0], item[0]) for item in get_all_lexers()])
|
|
||||||
STYLE_CHOICES = sorted((item, item) for item in list(get_all_styles()))
|
|
||||||
|
|
||||||
|
|
||||||
class PygmentsForm(forms.Form):
|
|
||||||
"""A simple form with some of the most important pygments settings.
|
|
||||||
The code to be highlighted can be specified either in a text field, or by URL.
|
|
||||||
We do some additional form validation to ensure clients see helpful error responses."""
|
|
||||||
|
|
||||||
code = forms.CharField(widget=forms.Textarea,
|
|
||||||
label='Code Text',
|
|
||||||
max_length=1000000,
|
|
||||||
help_text='(Copy and paste the code text here.)')
|
|
||||||
title = forms.CharField(required=False,
|
|
||||||
help_text='(Optional)',
|
|
||||||
max_length=100)
|
|
||||||
linenos = forms.BooleanField(label='Show Line Numbers',
|
|
||||||
required=False)
|
|
||||||
lexer = forms.ChoiceField(choices=LEXER_CHOICES,
|
|
||||||
initial='python')
|
|
||||||
style = forms.ChoiceField(choices=STYLE_CHOICES,
|
|
||||||
initial='friendly')
|
|
|
@ -1 +0,0 @@
|
||||||
#We need models.py otherwise the test framework complains (http://code.djangoproject.com/ticket/7198)
|
|
|
@ -1,46 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
from django.utils import simplejson as json
|
|
||||||
|
|
||||||
from djangorestframework.compat import RequestFactory
|
|
||||||
|
|
||||||
from pygments_api import views
|
|
||||||
import tempfile, shutil
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestPygmentsExample(TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.factory = RequestFactory()
|
|
||||||
self.temp_dir = tempfile.mkdtemp()
|
|
||||||
views.HIGHLIGHTED_CODE_DIR = self.temp_dir
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
try:
|
|
||||||
shutil.rmtree(self.temp_dir)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_get_to_root(self):
|
|
||||||
'''Just do a get on the base url'''
|
|
||||||
request = self.factory.get('/pygments')
|
|
||||||
view = views.PygmentsRoot.as_view()
|
|
||||||
response = view(request)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_snippets_datetime_sorted(self):
|
|
||||||
'''Pygments examples should be datetime sorted'''
|
|
||||||
locations = []
|
|
||||||
for snippet in 'abcdefghij': # String length must not exceed views.MAX_FILES, otherwise test fails
|
|
||||||
form_data = {'code': '%s' % snippet, 'style':'friendly', 'lexer':'python'}
|
|
||||||
request = self.factory.post('/pygments', data=form_data)
|
|
||||||
view = views.PygmentsRoot.as_view()
|
|
||||||
response = view(request)
|
|
||||||
locations.append(response.items()[2][1])
|
|
||||||
import time
|
|
||||||
time.sleep(.1)
|
|
||||||
request = self.factory.get('/pygments')
|
|
||||||
view = views.PygmentsRoot.as_view()
|
|
||||||
response = view(request)
|
|
||||||
response_locations = json.loads(response.content)
|
|
||||||
self.assertEquals(locations, response_locations)
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from pygments_api.views import PygmentsRoot, PygmentsInstance
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', PygmentsRoot.as_view(), name='pygments-root'),
|
|
||||||
url(r'^([a-zA-Z0-9-]+)/$', PygmentsInstance.as_view(), name='pygments-instance'),
|
|
||||||
)
|
|
|
@ -1,118 +0,0 @@
|
||||||
from __future__ import with_statement # for python 2.5
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
from djangorestframework.renderers import BaseRenderer
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework import status
|
|
||||||
|
|
||||||
from pygments.formatters import HtmlFormatter
|
|
||||||
from pygments.lexers import get_lexer_by_name
|
|
||||||
from pygments import highlight
|
|
||||||
|
|
||||||
from forms import PygmentsForm
|
|
||||||
|
|
||||||
import os
|
|
||||||
import uuid
|
|
||||||
import operator
|
|
||||||
|
|
||||||
# We need somewhere to store the code snippets that we highlight
|
|
||||||
HIGHLIGHTED_CODE_DIR = os.path.join(settings.MEDIA_ROOT, 'pygments')
|
|
||||||
MAX_FILES = 10
|
|
||||||
|
|
||||||
if not os.path.exists(HIGHLIGHTED_CODE_DIR):
|
|
||||||
os.makedirs(HIGHLIGHTED_CODE_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
def list_dir_sorted_by_ctime(dir):
|
|
||||||
"""
|
|
||||||
Return a list of files sorted by creation time
|
|
||||||
"""
|
|
||||||
filepaths = [os.path.join(dir, file)
|
|
||||||
for file in os.listdir(dir)
|
|
||||||
if not file.startswith('.')]
|
|
||||||
ctimes = [(path, os.path.getctime(path)) for path in filepaths]
|
|
||||||
ctimes = sorted(ctimes, key=operator.itemgetter(1), reverse=False)
|
|
||||||
return [filepath for filepath, ctime in ctimes]
|
|
||||||
|
|
||||||
|
|
||||||
def remove_oldest_files(dir, max_files):
|
|
||||||
"""
|
|
||||||
Remove the oldest files in a directory 'dir', leaving at most 'max_files' remaining.
|
|
||||||
We use this to limit the number of resources in the sandbox.
|
|
||||||
"""
|
|
||||||
[os.remove(path) for path in list_dir_sorted_by_ctime(dir)[max_files:]]
|
|
||||||
|
|
||||||
|
|
||||||
class HTMLRenderer(BaseRenderer):
|
|
||||||
"""
|
|
||||||
Basic renderer which just returns the content without any further serialization.
|
|
||||||
"""
|
|
||||||
media_type = 'text/html'
|
|
||||||
|
|
||||||
|
|
||||||
class PygmentsRoot(View):
|
|
||||||
"""
|
|
||||||
This example demonstrates a simple RESTful Web API around the awesome pygments library.
|
|
||||||
This top level resource is used to create highlighted code snippets, and to list all the existing code snippets.
|
|
||||||
"""
|
|
||||||
form = PygmentsForm
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
"""
|
|
||||||
Return a list of all currently existing snippets.
|
|
||||||
"""
|
|
||||||
unique_ids = [os.path.split(f)[1]
|
|
||||||
for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
|
|
||||||
urls = [reverse('pygments-instance', args=[unique_id], request=request)
|
|
||||||
for unique_id in unique_ids]
|
|
||||||
return Response(urls)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
"""
|
|
||||||
Create a new highlighed snippet and return it's location.
|
|
||||||
For the purposes of the sandbox example, also ensure we delete the oldest snippets if we have > MAX_FILES.
|
|
||||||
"""
|
|
||||||
unique_id = str(uuid.uuid1())
|
|
||||||
pathname = os.path.join(HIGHLIGHTED_CODE_DIR, unique_id)
|
|
||||||
|
|
||||||
lexer = get_lexer_by_name(self.CONTENT['lexer'])
|
|
||||||
linenos = 'table' if self.CONTENT['linenos'] else False
|
|
||||||
options = {'title': self.CONTENT['title']} if self.CONTENT['title'] else {}
|
|
||||||
formatter = HtmlFormatter(style=self.CONTENT['style'], linenos=linenos, full=True, **options)
|
|
||||||
|
|
||||||
with open(pathname, 'w') as outfile:
|
|
||||||
highlight(self.CONTENT['code'], lexer, formatter, outfile)
|
|
||||||
|
|
||||||
remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
|
|
||||||
|
|
||||||
location = reverse('pygments-instance', args=[unique_id], request=request)
|
|
||||||
return Response(status=status.HTTP_201_CREATED, headers={'Location': location})
|
|
||||||
|
|
||||||
|
|
||||||
class PygmentsInstance(View):
|
|
||||||
"""
|
|
||||||
Simply return the stored highlighted HTML file with the correct mime type.
|
|
||||||
This Resource only renders HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class.
|
|
||||||
"""
|
|
||||||
renderers = (HTMLRenderer, )
|
|
||||||
|
|
||||||
def get(self, request, unique_id):
|
|
||||||
"""
|
|
||||||
Return the highlighted snippet.
|
|
||||||
"""
|
|
||||||
pathname = os.path.join(HIGHLIGHTED_CODE_DIR, unique_id)
|
|
||||||
if not os.path.exists(pathname):
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
return Response(open(pathname, 'r').read())
|
|
||||||
|
|
||||||
def delete(self, request, unique_id):
|
|
||||||
"""
|
|
||||||
Delete the highlighted snippet.
|
|
||||||
"""
|
|
||||||
pathname = os.path.join(HIGHLIGHTED_CODE_DIR, unique_id)
|
|
||||||
if not os.path.exists(pathname):
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
os.remove(pathname)
|
|
||||||
return Response()
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
# Create your models here.
|
|
|
@ -1,9 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from requestexample.views import RequestExampleView, EchoRequestContentView
|
|
||||||
from examples.views import ProxyView
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', RequestExampleView.as_view(), name='request-example'),
|
|
||||||
url(r'^content$', ProxyView.as_view(view_class=EchoRequestContentView), name='request-content'),
|
|
||||||
)
|
|
|
@ -1,43 +0,0 @@
|
||||||
from djangorestframework.compat import View
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
from djangorestframework.mixins import RequestMixin
|
|
||||||
from djangorestframework.views import View as DRFView
|
|
||||||
from djangorestframework import parsers
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
|
|
||||||
|
|
||||||
class RequestExampleView(DRFView):
|
|
||||||
"""
|
|
||||||
A container view for request examples.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
return Response([{'name': 'request.DATA Example', 'url': reverse('request-content')},])
|
|
||||||
|
|
||||||
|
|
||||||
class MyBaseViewUsingEnhancedRequest(RequestMixin, View):
|
|
||||||
"""
|
|
||||||
Base view enabling the usage of enhanced requests with user defined views.
|
|
||||||
"""
|
|
||||||
|
|
||||||
parsers = parsers.DEFAULT_PARSERS
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.request = request = self.create_request(request)
|
|
||||||
return super(MyBaseViewUsingEnhancedRequest, self).dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class EchoRequestContentView(MyBaseViewUsingEnhancedRequest):
|
|
||||||
"""
|
|
||||||
A view that just reads the items in `request.DATA` and echoes them back.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
return HttpResponse(("Found %s in request.DATA, content : %s" %
|
|
||||||
(type(request.DATA), request.DATA)))
|
|
||||||
|
|
||||||
def put(self, request, *args, **kwargs):
|
|
||||||
return HttpResponse(("Found %s in request.DATA, content : %s" %
|
|
||||||
(type(request.DATA), request.DATA)))
|
|
|
@ -1,3 +0,0 @@
|
||||||
Pygments==1.4
|
|
||||||
Markdown==2.0.3
|
|
||||||
git+git://github.com/tomchristie/django-rest-framework.git
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Pygments for the code highlighting example,
|
|
||||||
# markdown for the docstring -> auto-documentation
|
|
||||||
|
|
||||||
Pygments==1.4
|
|
||||||
Markdown==2.0.3
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django import forms
|
|
||||||
|
|
||||||
|
|
||||||
class MyForm(forms.Form):
|
|
||||||
foo = forms.BooleanField(required=False)
|
|
||||||
bar = forms.IntegerField(help_text='Must be an integer.')
|
|
||||||
baz = forms.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from resourceexample.views import ExampleView, AnotherExampleView
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
url(r'^$', ExampleView.as_view(), name='example-resource'),
|
|
||||||
url(r'^(?P<num>[0-9]+)/$', AnotherExampleView.as_view(), name='another-example'),
|
|
||||||
)
|
|
|
@ -1,49 +0,0 @@
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
from djangorestframework import status
|
|
||||||
|
|
||||||
from resourceexample.forms import MyForm
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleView(View):
|
|
||||||
"""
|
|
||||||
A basic read-only view that points to 3 other views.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
"""
|
|
||||||
Handle GET requests, returning a list of URLs pointing to
|
|
||||||
three other views.
|
|
||||||
"""
|
|
||||||
resource_urls = [reverse('another-example',
|
|
||||||
kwargs={'num': num},
|
|
||||||
request=request)
|
|
||||||
for num in range(3)]
|
|
||||||
return Response({"Some other resources": resource_urls})
|
|
||||||
|
|
||||||
|
|
||||||
class AnotherExampleView(View):
|
|
||||||
"""
|
|
||||||
A basic view, that can handle GET and POST requests.
|
|
||||||
Applies some simple form validation on POST requests.
|
|
||||||
"""
|
|
||||||
form = MyForm
|
|
||||||
|
|
||||||
def get(self, request, num):
|
|
||||||
"""
|
|
||||||
Handle GET requests.
|
|
||||||
Returns a simple string indicating which view the GET request was for.
|
|
||||||
"""
|
|
||||||
if int(num) > 2:
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
return Response("GET request to AnotherExampleResource %s" % num)
|
|
||||||
|
|
||||||
def post(self, request, num):
|
|
||||||
"""
|
|
||||||
Handle POST requests, with form validation.
|
|
||||||
Returns a simple string indicating what content was supplied.
|
|
||||||
"""
|
|
||||||
if int(num) > 2:
|
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
return Response("POST request to AnotherExampleResource %s, with content: %s" % (num, repr(self.CONTENT)))
|
|
|
@ -1,44 +0,0 @@
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.test.utils import get_runner
|
|
||||||
from coverage import coverage
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run the tests for the examples and generate a coverage report."""
|
|
||||||
|
|
||||||
# Discover the list of all modules that we should test coverage for
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
cov_files = []
|
|
||||||
for (path, dirs, files) in os.walk(project_dir):
|
|
||||||
# Drop tests and runtests directories from the test coverage report
|
|
||||||
if os.path.basename(path) == 'tests' or os.path.basename(path) == 'runtests':
|
|
||||||
continue
|
|
||||||
cov_files.extend([os.path.join(path, file) for file in files if file.endswith('.py')])
|
|
||||||
TestRunner = get_runner(settings)
|
|
||||||
|
|
||||||
cov = coverage()
|
|
||||||
cov.erase()
|
|
||||||
cov.start()
|
|
||||||
if hasattr(TestRunner, 'func_name'):
|
|
||||||
# Pre 1.2 test runners were just functions,
|
|
||||||
# and did not support the 'failfast' option.
|
|
||||||
import warnings
|
|
||||||
warnings.warn(
|
|
||||||
'Function-based test runners are deprecated. Test runners should be classes with a run_tests() method.',
|
|
||||||
DeprecationWarning
|
|
||||||
)
|
|
||||||
failures = TestRunner(None)
|
|
||||||
else:
|
|
||||||
test_runner = TestRunner()
|
|
||||||
failures = test_runner.run_tests(['blogpost', 'pygments_api'])
|
|
||||||
|
|
||||||
cov.stop()
|
|
||||||
cov.report(cov_files)
|
|
||||||
cov.xml_report(cov_files)
|
|
||||||
sys.exit(failures)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,65 +0,0 @@
|
||||||
"""The root view for the examples provided with Django REST framework"""
|
|
||||||
|
|
||||||
from djangorestframework.reverse import reverse
|
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
|
|
||||||
|
|
||||||
class Sandbox(View):
|
|
||||||
"""
|
|
||||||
This is the sandbox for the examples provided with
|
|
||||||
[Django REST framework][1].
|
|
||||||
|
|
||||||
These examples are provided to help you get a better idea of some of the
|
|
||||||
features of RESTful APIs created using the framework.
|
|
||||||
|
|
||||||
All the example APIs allow anonymous access, and can be navigated either
|
|
||||||
through the browser or from the command line.
|
|
||||||
|
|
||||||
For example, to get the default representation using curl:
|
|
||||||
|
|
||||||
bash: curl -X GET http://rest.ep.io/
|
|
||||||
|
|
||||||
Or, to get the plaintext documentation represention:
|
|
||||||
|
|
||||||
bash: curl -X GET http://rest.ep.io/ -H 'Accept: text/plain'
|
|
||||||
|
|
||||||
The examples provided:
|
|
||||||
|
|
||||||
1. A basic example using the [Resource][2] class.
|
|
||||||
2. A basic example using the [ModelResource][3] class.
|
|
||||||
3. An basic example using Django 1.3's [class based views][4] and
|
|
||||||
djangorestframework's [RendererMixin][5].
|
|
||||||
4. A generic object store API.
|
|
||||||
5. A code highlighting API.
|
|
||||||
6. A blog posts and comments API.
|
|
||||||
7. A basic example using permissions.
|
|
||||||
8. A basic example using enhanced request.
|
|
||||||
|
|
||||||
Please feel free to browse, create, edit and delete the resources in
|
|
||||||
these examples.
|
|
||||||
|
|
||||||
[1]: http://django-rest-framework.org
|
|
||||||
[2]: http://django-rest-framework.org/library/resource.html
|
|
||||||
[3]: http://django-rest-framework.org/library/modelresource.html
|
|
||||||
[4]: http://docs.djangoproject.com/en/dev/topics/class-based-views/
|
|
||||||
[5]: http://django-rest-framework.org/library/renderers.html
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
return Response([
|
|
||||||
{'name': 'Simple Resource example',
|
|
||||||
'url': reverse('example-resource', request=request)},
|
|
||||||
{'name': 'Simple ModelResource example',
|
|
||||||
'url': reverse('model-resource-root', request=request)},
|
|
||||||
{'name': 'Simple Mixin-only example',
|
|
||||||
'url': reverse('mixin-view', request=request)},
|
|
||||||
{'name': 'Object store API',
|
|
||||||
'url': reverse('object-store-root', request=request)},
|
|
||||||
{'name': 'Code highlighting API',
|
|
||||||
'url': reverse('pygments-root', request=request)},
|
|
||||||
{'name': 'Blog posts API',
|
|
||||||
'url': reverse('blog-posts-root', request=request)},
|
|
||||||
{'name': 'Permissions example',
|
|
||||||
'url': reverse('permissions-example', request=request)},
|
|
||||||
])
|
|
|
@ -1,117 +0,0 @@
|
||||||
# Settings for djangorestframework examples project
|
|
||||||
import django
|
|
||||||
import os
|
|
||||||
|
|
||||||
DEBUG = True
|
|
||||||
TEMPLATE_DEBUG = DEBUG
|
|
||||||
|
|
||||||
ADMINS = (
|
|
||||||
# ('Your Name', 'your_email@domain.com'),
|
|
||||||
)
|
|
||||||
|
|
||||||
MANAGERS = ADMINS
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
|
||||||
'NAME': 'sqlite3.db', # Or path to database file if using sqlite3.
|
|
||||||
'USER': '', # Not used with sqlite3.
|
|
||||||
'PASSWORD': '', # Not used with sqlite3.
|
|
||||||
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
|
||||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
|
||||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
|
||||||
# although not all choices may be available on all operating systems.
|
|
||||||
# On Unix systems, a value of None will cause Django to use the same
|
|
||||||
# timezone as the operating system.
|
|
||||||
# If running in a Windows environment this must be set to the same as your
|
|
||||||
# system time zone.
|
|
||||||
TIME_ZONE = 'Europe/London'
|
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
|
||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
|
||||||
LANGUAGE_CODE = 'en-uk'
|
|
||||||
|
|
||||||
SITE_ID = 1
|
|
||||||
|
|
||||||
# If you set this to False, Django will make some optimizations so as not
|
|
||||||
# to load the internationalization machinery.
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
# If you set this to False, Django will not format dates, numbers and
|
|
||||||
# calendars according to the current locale
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
|
||||||
# Example: "/home/media/media.lawrence.com/"
|
|
||||||
# NOTE: Some of the djangorestframework examples use MEDIA_ROOT to store content.
|
|
||||||
MEDIA_ROOT = os.path.join(os.getenv('EPIO_DATA_DIRECTORY', '.'), 'media')
|
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
|
||||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
|
||||||
# NOTE: None of the djangorestframework examples serve media content via MEDIA_URL.
|
|
||||||
MEDIA_URL = '/uploads/'
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
|
||||||
SECRET_KEY = 't&9mru2_k$t8e2-9uq-wu2a1)9v*us&j3i#lsqkt(lbx*vh1cu'
|
|
||||||
|
|
||||||
# List of callables that know how to import templates from various sources.
|
|
||||||
TEMPLATE_LOADERS = (
|
|
||||||
'django.template.loaders.filesystem.Loader',
|
|
||||||
'django.template.loaders.app_directories.Loader',
|
|
||||||
# 'django.template.loaders.eggs.Loader',
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'urls'
|
|
||||||
|
|
||||||
TEMPLATE_DIRS = (
|
|
||||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
|
||||||
# Always use forward slashes, even on Windows.
|
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
|
||||||
)
|
|
||||||
|
|
||||||
if django.VERSION < (1, 3):
|
|
||||||
staticfiles = 'staticfiles'
|
|
||||||
else:
|
|
||||||
staticfiles = 'django.contrib.staticfiles'
|
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.sites',
|
|
||||||
staticfiles,
|
|
||||||
'django.contrib.messages',
|
|
||||||
|
|
||||||
'djangorestframework',
|
|
||||||
|
|
||||||
'resourceexample',
|
|
||||||
'modelresourceexample',
|
|
||||||
'objectstore',
|
|
||||||
'pygments_api',
|
|
||||||
'blogpost',
|
|
||||||
'permissionsexample',
|
|
||||||
'requestexample',
|
|
||||||
)
|
|
||||||
|
|
||||||
import os
|
|
||||||
if os.environ.get('HUDSON_URL', None):
|
|
||||||
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
|
|
||||||
TEST_OUTPUT_VERBOSE = True
|
|
||||||
TEST_OUTPUT_DESCRIPTIONS = True
|
|
||||||
TEST_OUTPUT_DIR = 'xmlrunner'
|
|
|
@ -1,21 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, include, url
|
|
||||||
from sandbox.views import Sandbox
|
|
||||||
try:
|
|
||||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
|
||||||
except ImportError: # Django <= 1.2
|
|
||||||
from staticfiles.urls import staticfiles_urlpatterns
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
(r'^$', Sandbox.as_view()),
|
|
||||||
(r'^resource-example/', include('resourceexample.urls')),
|
|
||||||
(r'^model-resource-example/', include('modelresourceexample.urls')),
|
|
||||||
(r'^mixin/', include('mixin.urls')),
|
|
||||||
(r'^object-store/', include('objectstore.urls')),
|
|
||||||
(r'^pygments/', include('pygments_api.urls')),
|
|
||||||
(r'^blog-post/', include('blogpost.urls')),
|
|
||||||
(r'^permissions-example/', include('permissionsexample.urls')),
|
|
||||||
url(r'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
|
|
||||||
)
|
|
||||||
|
|
||||||
urlpatterns += staticfiles_urlpatterns()
|
|
|
@ -1,32 +0,0 @@
|
||||||
from djangorestframework.views import View
|
|
||||||
from djangorestframework.response import Response
|
|
||||||
|
|
||||||
|
|
||||||
class ProxyView(View):
|
|
||||||
"""
|
|
||||||
A view that just acts as a proxy to call non-djangorestframework views, while still
|
|
||||||
displaying the browsable API interface.
|
|
||||||
"""
|
|
||||||
|
|
||||||
view_class = None
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.request = request = self.create_request(request)
|
|
||||||
if request.method in ['PUT', 'POST']:
|
|
||||||
self.response = self.view_class.as_view()(request, *args, **kwargs)
|
|
||||||
return super(ProxyView, self).dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
return Response()
|
|
||||||
|
|
||||||
def put(self, request, *args, **kwargs):
|
|
||||||
return Response(self.response.content)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
return Response(self.response.content)
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
return self.view_class.__name__
|
|
||||||
|
|
||||||
def get_description(self, html):
|
|
||||||
return self.view_class.__doc__
|
|
Loading…
Reference in New Issue
Block a user