Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
1e04790d
Commit
1e04790d
authored
May 16, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixing some of the last blocking issues
parent
e92002dd
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
235 additions
and
128 deletions
+235
-128
djangorestframework/mixins.py
+18
-1
djangorestframework/renderers.py
+2
-2
djangorestframework/resources.py
+66
-9
examples/blogpost/models.py
+1
-30
examples/blogpost/urls.py
+26
-6
examples/modelresourceexample/models.py
+5
-9
examples/modelresourceexample/urls.py
+10
-3
examples/modelresourceexample/views.py
+0
-16
examples/objectstore/views.py
+39
-25
examples/pygments_api/views.py
+47
-19
examples/resourceexample/views.py
+19
-6
examples/sandbox/views.py
+2
-2
No files found.
djangorestframework/mixins.py
View file @
1e04790d
...
@@ -516,7 +516,7 @@ class CreateModelMixin(object):
...
@@ -516,7 +516,7 @@ class CreateModelMixin(object):
instance
.
save
()
instance
.
save
()
headers
=
{}
headers
=
{}
if
hasattr
(
instance
,
'get_absolute_url'
):
if
hasattr
(
instance
,
'get_absolute_url'
):
headers
[
'Location'
]
=
instance
.
get_absolute_url
(
)
headers
[
'Location'
]
=
self
.
resource
(
self
)
.
url
(
instance
)
return
Response
(
status
.
HTTP_201_CREATED
,
instance
,
headers
)
return
Response
(
status
.
HTTP_201_CREATED
,
instance
,
headers
)
...
@@ -569,10 +569,27 @@ class ListModelMixin(object):
...
@@ -569,10 +569,27 @@ class ListModelMixin(object):
"""
"""
Behavior to list a set of model instances on GET requests
Behavior to list a set of model instances on GET requests
"""
"""
# NB. Not obvious to me if it would be better to set this on the resource?
#
# Presumably it's more useful to have on the view, because that way you can
# have multiple views across different querysets mapping to the same resource.
#
# Perhaps it ought to be:
#
# 1) View.queryset
# 2) if None fall back to Resource.queryset
# 3) if None fall back to Resource.model.objects.all()
#
# Any feedback welcomed.
queryset
=
None
queryset
=
None
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
queryset
=
self
.
queryset
if
self
.
queryset
else
self
.
resource
.
model
.
objects
.
all
()
queryset
=
self
.
queryset
if
self
.
queryset
else
self
.
resource
.
model
.
objects
.
all
()
ordering
=
getattr
(
self
.
resource
,
'ordering'
,
None
)
if
ordering
:
args
=
as_tuple
(
ordering
)
queryset
=
queryset
.
order_by
(
*
args
)
return
queryset
.
filter
(
**
kwargs
)
return
queryset
.
filter
(
**
kwargs
)
djangorestframework/renderers.py
View file @
1e04790d
...
@@ -251,8 +251,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -251,8 +251,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
'form'
:
form_instance
,
'form'
:
form_instance
,
'login_url'
:
login_url
,
'login_url'
:
login_url
,
'logout_url'
:
logout_url
,
'logout_url'
:
logout_url
,
'ACCEPT_PARAM'
:
self
.
view
.
_ACCEPT_QUERY_PARAM
,
'ACCEPT_PARAM'
:
getattr
(
self
.
view
,
'_ACCEPT_QUERY_PARAM'
,
None
)
,
'METHOD_PARAM'
:
self
.
view
.
_METHOD_PARAM
,
'METHOD_PARAM'
:
getattr
(
self
.
view
,
'_METHOD_PARAM'
,
None
)
,
'ADMIN_MEDIA_PREFIX'
:
settings
.
ADMIN_MEDIA_PREFIX
'ADMIN_MEDIA_PREFIX'
:
settings
.
ADMIN_MEDIA_PREFIX
})
})
...
...
djangorestframework/resources.py
View file @
1e04790d
...
@@ -5,6 +5,9 @@ from django.db.models.query import QuerySet
...
@@ -5,6 +5,9 @@ from django.db.models.query import QuerySet
from
django.db.models.fields.related
import
RelatedField
from
django.db.models.fields.related
import
RelatedField
from
django.utils.encoding
import
smart_unicode
from
django.utils.encoding
import
smart_unicode
from
djangorestframework.response
import
ErrorResponse
from
djangorestframework.utils
import
as_tuple
import
decimal
import
decimal
import
inspect
import
inspect
import
re
import
re
...
@@ -12,6 +15,10 @@ import re
...
@@ -12,6 +15,10 @@ import re
# TODO: _IgnoreFieldException
# TODO: _IgnoreFieldException
# Map model classes to resource classes
#_model_to_resource = {}
def
_model_to_dict
(
instance
,
resource
=
None
):
def
_model_to_dict
(
instance
,
resource
=
None
):
"""
"""
Given a model instance, return a ``dict`` representing the model.
Given a model instance, return a ``dict`` representing the model.
...
@@ -179,18 +186,31 @@ class FormResource(Resource):
...
@@ -179,18 +186,31 @@ class FormResource(Resource):
return
self
.
_validate
(
data
,
files
)
return
self
.
_validate
(
data
,
files
)
def
_validate
(
self
,
data
,
files
,
allowed_extra_fields
=
()):
def
_validate
(
self
,
data
,
files
,
allowed_extra_fields
=
()
,
fake_data
=
None
):
"""
"""
Wrapped by validate to hide the extra_fields option that the ModelValidatorMixin uses.
Wrapped by validate to hide the extra_fields option that the ModelValidatorMixin uses.
extra_fields is a list of fields which are not defined by the form, but which we still
extra_fields is a list of fields which are not defined by the form, but which we still
expect to see on the input.
expect to see on the input.
"""
"""
# We'd like nice error messages even if no content is supplied.
# Typically if an empty dict is given to a form Django will
# return .is_valid() == False, but .errors == {}
#
# To get around this case we revalidate with some fake data.
if
fake_data
:
data
[
fake_data
]
=
'_fake_data'
allowed_extra_fields
=
allowed_extra_fields
+
(
'_fake_data'
,)
bound_form
=
self
.
get_bound_form
(
data
,
files
)
bound_form
=
self
.
get_bound_form
(
data
,
files
)
if
bound_form
is
None
:
if
bound_form
is
None
:
return
data
return
data
self
.
view
.
bound_form_instance
=
bound_form
self
.
view
.
bound_form_instance
=
bound_form
data
=
data
and
data
or
{}
files
=
files
and
files
or
{}
seen_fields_set
=
set
(
data
.
keys
())
seen_fields_set
=
set
(
data
.
keys
())
form_fields_set
=
set
(
bound_form
.
fields
.
keys
())
form_fields_set
=
set
(
bound_form
.
fields
.
keys
())
...
@@ -198,6 +218,7 @@ class FormResource(Resource):
...
@@ -198,6 +218,7 @@ class FormResource(Resource):
# In addition to regular validation we also ensure no additional fields are being passed in...
# In addition to regular validation we also ensure no additional fields are being passed in...
unknown_fields
=
seen_fields_set
-
(
form_fields_set
|
allowed_extra_fields_set
)
unknown_fields
=
seen_fields_set
-
(
form_fields_set
|
allowed_extra_fields_set
)
unknown_fields
=
unknown_fields
-
set
((
'csrfmiddlewaretoken'
,))
# Check using both regular validation, and our stricter no additional fields rule
# Check using both regular validation, and our stricter no additional fields rule
if
bound_form
.
is_valid
()
and
not
unknown_fields
:
if
bound_form
.
is_valid
()
and
not
unknown_fields
:
...
@@ -216,6 +237,13 @@ class FormResource(Resource):
...
@@ -216,6 +237,13 @@ class FormResource(Resource):
detail
=
{}
detail
=
{}
if
not
bound_form
.
errors
and
not
unknown_fields
:
if
not
bound_form
.
errors
and
not
unknown_fields
:
# is_valid() was False, but errors was empty.
# If we havn't already done so attempt revalidation with some fake data
# to force django to give us an errors dict.
if
fake_data
is
None
:
return
self
.
_validate
(
data
,
files
,
allowed_extra_fields
,
'_fake_data'
)
# If we've already set fake_dict and we're still here, fallback gracefully.
detail
=
{
u'errors'
:
[
u'No content was supplied.'
]}
detail
=
{
u'errors'
:
[
u'No content was supplied.'
]}
else
:
else
:
...
@@ -256,12 +284,29 @@ class FormResource(Resource):
...
@@ -256,12 +284,29 @@ class FormResource(Resource):
return
self
.
form
()
return
self
.
form
()
#class _RegisterModelResource(type):
# """
# Auto register new ModelResource classes into ``_model_to_resource``
# """
# def __new__(cls, name, bases, dct):
# resource_cls = type.__new__(cls, name, bases, dct)
# model_cls = dct.get('model', None)
# if model_cls:
# _model_to_resource[model_cls] = resource_cls
# return resource_cls
class
ModelResource
(
FormResource
):
class
ModelResource
(
FormResource
):
"""
"""
Resource class that uses forms for validation and otherwise falls back to a model form if no form is set.
Resource class that uses forms for validation and otherwise falls back to a model form if no form is set.
Also provides a get_bound_form() method which may be used by some renderers.
Also provides a get_bound_form() method which may be used by some renderers.
"""
"""
# Auto-register new ModelResource classes into _model_to_resource
#__metaclass__ = _RegisterModelResource
"""
"""
The form class that should be used for request validation.
The form class that should be used for request validation.
If set to ``None`` then the default model form validation will be used.
If set to ``None`` then the default model form validation will be used.
...
@@ -314,7 +359,7 @@ class ModelResource(FormResource):
...
@@ -314,7 +359,7 @@ class ModelResource(FormResource):
return
self
.
_validate
(
data
,
files
,
allowed_extra_fields
=
self
.
_property_fields_set
)
return
self
.
_validate
(
data
,
files
,
allowed_extra_fields
=
self
.
_property_fields_set
)
def
get_bound_form
(
self
,
content
=
None
):
def
get_bound_form
(
self
,
data
=
None
,
files
=
None
):
"""
"""
Given some content return a ``Form`` instance bound to that content.
Given some content return a ``Form`` instance bound to that content.
...
@@ -334,11 +379,11 @@ class ModelResource(FormResource):
...
@@ -334,11 +379,11 @@ class ModelResource(FormResource):
#fields = tuple(self._model_fields_set)
#fields = tuple(self._model_fields_set)
# Instantiate the ModelForm as appropriate
# Instantiate the ModelForm as appropriate
if
content
and
isinstance
(
content
,
models
.
Model
):
if
data
and
isinstance
(
data
,
models
.
Model
):
# Bound to an existing model instance
# Bound to an existing model instance
return
OnTheFlyModelForm
(
instance
=
content
)
return
OnTheFlyModelForm
(
instance
=
content
)
elif
content
is
not
None
:
elif
data
is
not
None
:
return
OnTheFlyModelForm
(
content
)
return
OnTheFlyModelForm
(
data
,
files
)
return
OnTheFlyModelForm
()
return
OnTheFlyModelForm
()
# Both form and model not set? Okay bruv, whatevs...
# Both form and model not set? Okay bruv, whatevs...
...
@@ -364,7 +409,19 @@ class ModelResource(FormResource):
...
@@ -364,7 +409,19 @@ class ModelResource(FormResource):
# pattern = tuple_item[1]
# pattern = tuple_item[1]
# Note: defaults = tuple_item[2] for django >= 1.3
# Note: defaults = tuple_item[2] for django >= 1.3
for
result
,
params
in
possibility
:
for
result
,
params
in
possibility
:
instance_attrs
=
dict
([
(
param
,
getattr
(
instance
,
param
))
for
param
in
params
if
hasattr
(
instance
,
param
)
])
#instance_attrs = dict([ (param, getattr(instance, param)) for param in params if hasattr(instance, param) ])
instance_attrs
=
{}
for
param
in
params
:
if
not
hasattr
(
instance
,
param
):
continue
attr
=
getattr
(
instance
,
param
)
if
isinstance
(
attr
,
models
.
Model
):
instance_attrs
[
param
]
=
attr
.
pk
else
:
instance_attrs
[
param
]
=
attr
try
:
try
:
return
reverse
(
self
.
view_callable
[
0
],
kwargs
=
instance_attrs
)
return
reverse
(
self
.
view_callable
[
0
],
kwargs
=
instance_attrs
)
except
NoReverseMatch
:
except
NoReverseMatch
:
...
@@ -393,7 +450,7 @@ class ModelResource(FormResource):
...
@@ -393,7 +450,7 @@ class ModelResource(FormResource):
isinstance
(
getattr
(
self
.
model
,
attr
,
None
),
property
)
isinstance
(
getattr
(
self
.
model
,
attr
,
None
),
property
)
and
not
attr
.
startswith
(
'_'
))
and
not
attr
.
startswith
(
'_'
))
if
fields
:
if
self
.
fields
:
return
property_fields
&
set
(
as_tuple
(
self
.
fields
))
return
property_fields
&
set
(
as_tuple
(
self
.
fields
))
return
property_fields
-
set
(
as_tuple
(
self
.
exclude
))
return
property_fields
.
union
(
set
(
as_tuple
(
self
.
include
)))
-
set
(
as_tuple
(
self
.
exclude
))
examples/blogpost/models.py
View file @
1e04790d
...
@@ -21,26 +21,10 @@ class BlogPost(models.Model):
...
@@ -21,26 +21,10 @@ class BlogPost(models.Model):
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
slug
=
models
.
SlugField
(
editable
=
False
,
default
=
''
)
slug
=
models
.
SlugField
(
editable
=
False
,
default
=
''
)
class
Meta
:
ordering
=
(
'created'
,)
@models.permalink
def
get_absolute_url
(
self
):
return
(
'blog-post'
,
(),
{
'key'
:
self
.
key
})
@property
@models.permalink
def
comments_url
(
self
):
"""Link to a resource which lists all comments for this blog post."""
return
(
'comments'
,
(),
{
'blogpost'
:
self
.
key
})
def
__unicode__
(
self
):
return
self
.
title
def
save
(
self
,
*
args
,
**
kwargs
):
def
save
(
self
,
*
args
,
**
kwargs
):
self
.
slug
=
slugify
(
self
.
title
)
self
.
slug
=
slugify
(
self
.
title
)
super
(
self
.
__class__
,
self
)
.
save
(
*
args
,
**
kwargs
)
super
(
self
.
__class__
,
self
)
.
save
(
*
args
,
**
kwargs
)
for
obj
in
self
.
__class__
.
objects
.
order_by
(
'-
pk
'
)[
MAX_POSTS
:]:
for
obj
in
self
.
__class__
.
objects
.
order_by
(
'-
created
'
)[
MAX_POSTS
:]:
obj
.
delete
()
obj
.
delete
()
...
@@ -51,16 +35,3 @@ class Comment(models.Model):
...
@@ -51,16 +35,3 @@ class Comment(models.Model):
rating
=
models
.
IntegerField
(
blank
=
True
,
null
=
True
,
choices
=
RATING_CHOICES
,
help_text
=
'How did you rate this post?'
)
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
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
class
Meta
:
ordering
=
(
'created'
,)
@models.permalink
def
get_absolute_url
(
self
):
return
(
'comment'
,
(),
{
'blogpost'
:
self
.
blogpost
.
key
,
'id'
:
self
.
id
})
@property
@models.permalink
def
blogpost_url
(
self
):
"""Link to the blog post resource which this comment corresponds to."""
return
(
'blog-post'
,
(),
{
'key'
:
self
.
blogpost
.
key
})
examples/blogpost/urls.py
View file @
1e04790d
from
django.conf.urls.defaults
import
patterns
,
url
from
django.conf.urls.defaults
import
patterns
,
url
from
blogpost.views
import
BlogPosts
,
BlogPostInstance
,
Comments
,
CommentInstance
from
django.core.urlresolvers
import
reverse
from
djangorestframework.views
import
ListOrCreateModelView
,
InstanceModelView
from
djangorestframework.resources
import
ModelResource
from
blogpost.models
import
BlogPost
,
Comment
class
BlogPostResource
(
ModelResource
):
model
=
BlogPost
fields
=
(
'created'
,
'title'
,
'slug'
,
'content'
,
'url'
,
'comments'
)
ordering
=
(
'-created'
,)
def
comments
(
self
,
instance
):
return
reverse
(
'comments'
,
kwargs
=
{
'blogpost'
:
instance
.
key
})
class
CommentResource
(
ModelResource
):
model
=
Comment
fields
=
(
'username'
,
'comment'
,
'created'
,
'rating'
,
'url'
,
'blogpost'
)
ordering
=
(
'-created'
,)
urlpatterns
=
patterns
(
''
,
urlpatterns
=
patterns
(
''
,
url
(
r'^$'
,
BlogPosts
.
as_view
(),
name
=
'blog-posts
'
),
url
(
r'^$'
,
ListOrCreateModelView
.
as_view
(
resource
=
BlogPostResource
),
name
=
'blog-posts-root
'
),
url
(
r'^(?P<key>[^/]+)/$'
,
BlogPostInstance
.
as_view
(),
name
=
'blog-post'
),
url
(
r'^(?P<key>[^/]+)/$'
,
InstanceModelView
.
as_view
(
resource
=
BlogPostResource
)
),
url
(
r'^(?P<blogpost>[^/]+)/comments/$'
,
Comments
.
as_view
(
),
name
=
'comments'
),
url
(
r'^(?P<blogpost>[^/]+)/comments/$'
,
ListOrCreateModelView
.
as_view
(
resource
=
CommentResource
),
name
=
'comments'
),
url
(
r'^(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$'
,
CommentInstance
.
as_view
(),
name
=
'comment'
),
url
(
r'^(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$'
,
InstanceModelView
.
as_view
(
resource
=
CommentResource
)
),
)
)
\ No newline at end of file
examples/modelresourceexample/models.py
View file @
1e04790d
...
@@ -7,17 +7,13 @@ class MyModel(models.Model):
...
@@ -7,17 +7,13 @@ class MyModel(models.Model):
bar
=
models
.
IntegerField
(
help_text
=
'Must be an integer.'
)
bar
=
models
.
IntegerField
(
help_text
=
'Must be an integer.'
)
baz
=
models
.
CharField
(
max_length
=
32
,
help_text
=
'Free text. Max length 32 chars.'
)
baz
=
models
.
CharField
(
max_length
=
32
,
help_text
=
'Free text. Max length 32 chars.'
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
class
Meta
:
ordering
=
(
'created'
,)
def
save
(
self
,
*
args
,
**
kwargs
):
def
save
(
self
,
*
args
,
**
kwargs
):
"""For the purposes of the sandbox, limit the maximum number of stored models."""
"""
For the purposes of the sandbox limit the maximum number of stored models.
"""
super
(
MyModel
,
self
)
.
save
(
*
args
,
**
kwargs
)
super
(
MyModel
,
self
)
.
save
(
*
args
,
**
kwargs
)
while
MyModel
.
objects
.
all
()
.
count
()
>
MAX_INSTANCES
:
while
MyModel
.
objects
.
all
()
.
count
()
>
MAX_INSTANCES
:
MyModel
.
objects
.
all
()[
0
]
.
delete
()
MyModel
.
objects
.
all
()
.
order_by
(
'-created'
)[
0
]
.
delete
()
@models.permalink
def
get_absolute_url
(
self
):
return
(
'my-model-resource'
,
(
self
.
pk
,))
examples/modelresourceexample/urls.py
View file @
1e04790d
from
django.conf.urls.defaults
import
patterns
,
url
from
django.conf.urls.defaults
import
patterns
,
url
from
modelresourceexample.views
import
MyModelRootResource
,
MyModelResource
from
djangorestframework.views
import
ListOrCreateModelView
,
InstanceModelView
from
djangorestframework.resources
import
ModelResource
from
modelresourceexample.models
import
MyModel
class
MyModelResource
(
ModelResource
):
model
=
MyModel
fields
=
(
'foo'
,
'bar'
,
'baz'
,
'url'
)
ordering
=
(
'created'
,)
urlpatterns
=
patterns
(
'modelresourceexample.views'
,
urlpatterns
=
patterns
(
'modelresourceexample.views'
,
url
(
r'^$'
,
MyModelRootResource
.
as_view
(),
name
=
'my-model-root-resource
'
),
url
(
r'^$'
,
ListOrCreateModelView
.
as_view
(
resource
=
MyModelResource
),
name
=
'model-resource-root
'
),
url
(
r'^([0-9]+)/$'
,
MyModelResource
.
as_view
(),
name
=
'my-model-resource'
),
url
(
r'^([0-9]+)/$'
,
InstanceModelView
.
as_view
(
resource
=
MyModelResource
)
),
)
)
examples/modelresourceexample/views.py
View file @
1e04790d
from
djangorestframework.modelresource
import
InstanceModelResource
,
ListOrCreateModelResource
from
modelresourceexample.models
import
MyModel
FIELDS
=
(
'foo'
,
'bar'
,
'baz'
,
'absolute_url'
)
class
MyModelRootResource
(
ListOrCreateModelResource
):
"""A create/list resource for MyModel.
Available for both authenticated and anonymous access for the purposes of the sandbox."""
model
=
MyModel
fields
=
FIELDS
class
MyModelResource
(
InstanceModelResource
):
"""A read/update/delete resource for MyModel.
Available for both authenticated and anonymous access for the purposes of the sandbox."""
model
=
MyModel
fields
=
FIELDS
examples/objectstore/views.py
View file @
1e04790d
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
djangorestframework.
resource
import
Resource
from
djangorestframework.
views
import
BaseView
from
djangorestframework.response
import
Response
from
djangorestframework.response
import
Response
from
djangorestframework
import
status
from
djangorestframework
import
status
...
@@ -15,55 +15,69 @@ MAX_FILES = 10
...
@@ -15,55 +15,69 @@ MAX_FILES = 10
def
remove_oldest_files
(
dir
,
max_files
):
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."""
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
(
'.'
)]
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
],
ctime_sorted_paths
=
[
item
[
0
]
for
item
in
sorted
([(
path
,
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
],
key
=
operator
.
itemgetter
(
1
),
reverse
=
True
)]
key
=
operator
.
itemgetter
(
1
),
reverse
=
True
)]
[
os
.
remove
(
path
)
for
path
in
ctime_sorted_paths
[
max_files
:]]
[
os
.
remove
(
path
)
for
path
in
ctime_sorted_paths
[
max_files
:]]
class
ObjectStoreRoot
(
Resource
):
class
ObjectStoreRoot
(
BaseView
):
"""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."""
Root of the Object Store API.
allowed_methods
=
anon_allowed_methods
=
(
'GET'
,
'POST'
)
Allows the client to get a complete list of all the stored objects, or to create a new stored object.
"""
def
get
(
self
,
request
,
auth
):
def
get
(
self
,
request
):
"""Return a list of all the stored object URLs. (Ordered by creation time, newest first)"""
"""
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
(
'.'
)]
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
],
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
)]
key
=
operator
.
itemgetter
(
1
),
reverse
=
True
)]
return
[
reverse
(
'stored-object'
,
kwargs
=
{
'key'
:
key
})
for
key
in
ctime_sorted_basenames
]
return
[
reverse
(
'stored-object'
,
kwargs
=
{
'key'
:
key
})
for
key
in
ctime_sorted_basenames
]
def
post
(
self
,
request
,
auth
,
content
):
def
post
(
self
,
request
):
"""Create a new stored object, with a unique key."""
"""
Create a new stored object, with a unique key.
"""
key
=
str
(
uuid
.
uuid1
())
key
=
str
(
uuid
.
uuid1
())
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pickle
.
dump
(
content
,
open
(
pathname
,
'wb'
))
pickle
.
dump
(
self
.
CONTENT
,
open
(
pathname
,
'wb'
))
remove_oldest_files
(
OBJECT_STORE_DIR
,
MAX_FILES
)
remove_oldest_files
(
OBJECT_STORE_DIR
,
MAX_FILES
)
return
Response
(
status
.
HTTP_201_CREATED
,
content
,
{
'Location'
:
reverse
(
'stored-object'
,
kwargs
=
{
'key'
:
key
})})
return
Response
(
status
.
HTTP_201_CREATED
,
self
.
CONTENT
,
{
'Location'
:
reverse
(
'stored-object'
,
kwargs
=
{
'key'
:
key
})})
class
StoredObject
(
Resource
):
class
StoredObject
(
BaseView
):
"""Represents a stored object.
"""
The object may be any picklable content."""
Represents a stored object.
allowed_methods
=
anon_allowed_methods
=
(
'GET'
,
'PUT'
,
'DELETE'
)
The object may be any picklable content.
"""
def
get
(
self
,
request
,
auth
,
key
):
def
get
(
self
,
request
,
key
):
"""Return a stored object, by unpickling the contents of a locally stored file."""
"""
Return a stored object, by unpickling the contents of a locally stored file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
if
not
os
.
path
.
exists
(
pathname
):
if
not
os
.
path
.
exists
(
pathname
):
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
pickle
.
load
(
open
(
pathname
,
'rb'
))
return
pickle
.
load
(
open
(
pathname
,
'rb'
))
def
put
(
self
,
request
,
auth
,
content
,
key
):
def
put
(
self
,
request
,
key
):
"""Update/create a stored object, by pickling the request content to a locally stored file."""
"""
Update/create a stored object, by pickling the request content to a locally stored file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pickle
.
dump
(
content
,
open
(
pathname
,
'wb'
))
pickle
.
dump
(
self
.
CONTENT
,
open
(
pathname
,
'wb'
))
return
content
return
self
.
CONTENT
def
delete
(
self
,
request
,
auth
,
key
):
def
delete
(
self
,
request
):
"""Delete a stored object, by removing it's pickled file."""
"""
Delete a stored object, by removing it's pickled file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
if
not
os
.
path
.
exists
(
pathname
):
if
not
os
.
path
.
exists
(
pathname
):
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
...
...
examples/pygments_api/views.py
View file @
1e04790d
...
@@ -2,9 +2,10 @@ from __future__ import with_statement # for python 2.5
...
@@ -2,9 +2,10 @@ from __future__ import with_statement # for python 2.5
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
djangorestframework.resource
import
Resource
from
djangorestframework.resource
s
import
Form
Resource
from
djangorestframework.response
import
Response
from
djangorestframework.response
import
Response
from
djangorestframework.renderers
import
BaseRenderer
from
djangorestframework.renderers
import
BaseRenderer
from
djangorestframework.views
import
BaseView
from
djangorestframework
import
status
from
djangorestframework
import
status
from
pygments.formatters
import
HtmlFormatter
from
pygments.formatters
import
HtmlFormatter
...
@@ -17,39 +18,60 @@ import os
...
@@ -17,39 +18,60 @@ import os
import
uuid
import
uuid
import
operator
import
operator
# We need somewhere to store the code that we highlight
# We need somewhere to store the code
snippets
that we highlight
HIGHLIGHTED_CODE_DIR
=
os
.
path
.
join
(
settings
.
MEDIA_ROOT
,
'pygments'
)
HIGHLIGHTED_CODE_DIR
=
os
.
path
.
join
(
settings
.
MEDIA_ROOT
,
'pygments'
)
MAX_FILES
=
10
MAX_FILES
=
10
def
list_dir_sorted_by_ctime
(
dir
):
def
list_dir_sorted_by_ctime
(
dir
):
"""Return a list of files sorted by creation time"""
"""
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
(
'.'
)]
filepaths
=
[
os
.
path
.
join
(
dir
,
file
)
for
file
in
os
.
listdir
(
dir
)
if
not
file
.
startswith
(
'.'
)]
return
[
item
[
0
]
for
item
in
sorted
([(
path
,
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
],
return
[
item
[
0
]
for
item
in
sorted
(
[(
path
,
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
],
key
=
operator
.
itemgetter
(
1
),
reverse
=
False
)]
key
=
operator
.
itemgetter
(
1
),
reverse
=
False
)
]
def
remove_oldest_files
(
dir
,
max_files
):
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."""
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
:]]
[
os
.
remove
(
path
)
for
path
in
list_dir_sorted_by_ctime
(
dir
)[
max_files
:]]
class
HTMLRenderer
(
BaseRenderer
):
class
HTMLRenderer
(
BaseRenderer
):
"""Basic renderer which just returns the content without any further serialization."""
"""
Basic renderer which just returns the content without any further serialization.
"""
media_type
=
'text/html'
media_type
=
'text/html'
class
PygmentsRoot
(
Resource
):
"""This example demonstrates a simple RESTful Web API aound the awesome pygments library.
class
PygmentsFormResource
(
FormResource
):
This top level resource is used to create highlighted code snippets, and to list all the existing code snippets."""
"""
"""
form
=
PygmentsForm
form
=
PygmentsForm
class
PygmentsRoot
(
BaseView
):
"""
This example demonstrates a simple RESTful Web API aound the awesome pygments library.
This top level resource is used to create highlighted code snippets, and to list all the existing code snippets.
"""
resource
=
PygmentsFormResource
def
get
(
self
,
request
):
def
get
(
self
,
request
):
"""Return a list of all currently existing snippets."""
"""
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
)]
unique_ids
=
[
os
.
path
.
split
(
f
)[
1
]
for
f
in
list_dir_sorted_by_ctime
(
HIGHLIGHTED_CODE_DIR
)]
return
[
reverse
(
'pygments-instance'
,
args
=
[
unique_id
])
for
unique_id
in
unique_ids
]
return
[
reverse
(
'pygments-instance'
,
args
=
[
unique_id
])
for
unique_id
in
unique_ids
]
def
post
(
self
,
request
):
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."""
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
())
unique_id
=
str
(
uuid
.
uuid1
())
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
...
@@ -66,20 +88,26 @@ class PygmentsRoot(Resource):
...
@@ -66,20 +88,26 @@ class PygmentsRoot(Resource):
return
Response
(
status
.
HTTP_201_CREATED
,
headers
=
{
'Location'
:
reverse
(
'pygments-instance'
,
args
=
[
unique_id
])})
return
Response
(
status
.
HTTP_201_CREATED
,
headers
=
{
'Location'
:
reverse
(
'pygments-instance'
,
args
=
[
unique_id
])})
class
PygmentsInstance
(
Resource
):
class
PygmentsInstance
(
BaseView
):
"""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."""
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
,)
renderers
=
(
HTMLRenderer
,)
def
get
(
self
,
request
,
unique_id
):
def
get
(
self
,
request
,
unique_id
):
"""Return the highlighted snippet."""
"""
Return the highlighted snippet.
"""
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
if
not
os
.
path
.
exists
(
pathname
):
if
not
os
.
path
.
exists
(
pathname
):
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
open
(
pathname
,
'r'
)
.
read
()
return
open
(
pathname
,
'r'
)
.
read
()
def
delete
(
self
,
request
,
unique_id
):
def
delete
(
self
,
request
,
unique_id
):
"""Delete the highlighted snippet."""
"""
Delete the highlighted snippet.
"""
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
pathname
=
os
.
path
.
join
(
HIGHLIGHTED_CODE_DIR
,
unique_id
)
if
not
os
.
path
.
exists
(
pathname
):
if
not
os
.
path
.
exists
(
pathname
):
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
status
.
HTTP_404_NOT_FOUND
)
...
...
examples/resourceexample/views.py
View file @
1e04790d
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
djangorestframework.resource
import
Resource
from
djangorestframework.views
import
BaseView
from
djangorestframework.resources
import
FormResource
from
djangorestframework.response
import
Response
from
djangorestframework.response
import
Response
from
djangorestframework
import
status
from
djangorestframework
import
status
from
resourceexample.forms
import
MyForm
from
resourceexample.forms
import
MyForm
class
ExampleResource
(
Resource
):
class
MyFormValidation
(
FormResource
):
"""A basic read-only resource that points to 3 other resources."""
"""
A resource which applies form validation on the input.
"""
form
=
MyForm
class
ExampleResource
(
BaseView
):
"""
A basic read-only resource that points to 3 other resources.
"""
def
get
(
self
,
request
):
def
get
(
self
,
request
):
return
{
"Some other resources"
:
[
reverse
(
'another-example-resource'
,
kwargs
=
{
'num'
:
num
})
for
num
in
range
(
3
)]}
return
{
"Some other resources"
:
[
reverse
(
'another-example-resource'
,
kwargs
=
{
'num'
:
num
})
for
num
in
range
(
3
)]}
class
AnotherExampleResource
(
Resource
):
"""A basic GET-able/POST-able resource."""
class
AnotherExampleResource
(
BaseView
):
form
=
MyForm
# Optional form validation on input (Applies in this case the POST method, but can also apply to PUT)
"""
A basic GET-able/POST-able resource.
"""
resource
=
MyFormValidation
def
get
(
self
,
request
,
num
):
def
get
(
self
,
request
,
num
):
"""Handle GET requests"""
"""Handle GET requests"""
...
...
examples/sandbox/views.py
View file @
1e04790d
...
@@ -27,8 +27,8 @@ class Sandbox(BaseView):
...
@@ -27,8 +27,8 @@ class Sandbox(BaseView):
def
get
(
self
,
request
):
def
get
(
self
,
request
):
return
[{
'name'
:
'Simple Resource example'
,
'url'
:
reverse
(
'example-resource'
)},
return
[{
'name'
:
'Simple Resource example'
,
'url'
:
reverse
(
'example-resource'
)},
{
'name'
:
'Simple ModelResource example'
,
'url'
:
reverse
(
'm
y-model-root-resource
'
)},
{
'name'
:
'Simple ModelResource example'
,
'url'
:
reverse
(
'm
odel-resource-root
'
)},
{
'name'
:
'Simple Mixin-only example'
,
'url'
:
reverse
(
'mixin-view'
)},
{
'name'
:
'Simple Mixin-only example'
,
'url'
:
reverse
(
'mixin-view'
)},
{
'name'
:
'Object store API'
,
'url'
:
reverse
(
'object-store-root'
)},
{
'name'
:
'Object store API'
,
'url'
:
reverse
(
'object-store-root'
)},
{
'name'
:
'Code highlighting API'
,
'url'
:
reverse
(
'pygments-root'
)},
{
'name'
:
'Code highlighting API'
,
'url'
:
reverse
(
'pygments-root'
)},
{
'name'
:
'Blog posts API'
,
'url'
:
reverse
(
'blog-posts'
)}]
{
'name'
:
'Blog posts API'
,
'url'
:
reverse
(
'blog-posts
-root
'
)}]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment