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
325e63a3
Commit
325e63a3
authored
May 13, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Sorting out resources. Doing some crazy magic automatic url resolving stuff. Yum.
parent
8f6bcac7
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
192 additions
and
340 deletions
+192
-340
djangorestframework/mixins.py
+54
-7
djangorestframework/resources.py
+130
-328
djangorestframework/tests/resources.py
+0
-0
djangorestframework/utils/mediatypes.py
+1
-0
djangorestframework/views.py
+7
-5
No files found.
djangorestframework/mixins.py
View file @
325e63a3
...
@@ -25,6 +25,8 @@ __all__ = (
...
@@ -25,6 +25,8 @@ __all__ = (
'ResponseMixin'
,
'ResponseMixin'
,
'AuthMixin'
,
'AuthMixin'
,
'ResourceMixin'
,
'ResourceMixin'
,
#
'InstanceMixin'
,
# Model behavior mixins
# Model behavior mixins
'ReadModelMixin'
,
'ReadModelMixin'
,
'CreateModelMixin'
,
'CreateModelMixin'
,
...
@@ -137,7 +139,7 @@ class RequestMixin(object):
...
@@ -137,7 +139,7 @@ class RequestMixin(object):
content_length
=
0
content_length
=
0
# TODO: Add 1.3's LimitedStream to compat and use that.
# TODO: Add 1.3's LimitedStream to compat and use that.
# Currently only supports parsing request body as a stream with 1.3
#
NOTE:
Currently only supports parsing request body as a stream with 1.3
if
content_length
==
0
:
if
content_length
==
0
:
return
None
return
None
elif
hasattr
(
request
,
'read'
):
elif
hasattr
(
request
,
'read'
):
...
@@ -405,26 +407,71 @@ class AuthMixin(object):
...
@@ -405,26 +407,71 @@ class AuthMixin(object):
permission
.
check_permission
(
user
)
permission
.
check_permission
(
user
)
##########
class
InstanceMixin
(
object
):
"""
Mixin class that is used to identify a view class as being the canonical identifier
for the resources it is mapped too.
"""
@classmethod
def
as_view
(
cls
,
**
initkwargs
):
"""
Store the callable object on the resource class that has been associated with this view.
"""
view
=
super
(
InstanceMixin
,
cls
)
.
as_view
(
**
initkwargs
)
if
'resource'
in
initkwargs
:
# We do a little dance when we store the view callable...
# we need to store it wrapped in a 1-tuple, so that inspect will treat it
# as a function when we later look it up (rather than turning it into a method).
# This makes sure our URL reversing works ok.
initkwargs
[
'resource'
]
.
view_callable
=
(
view
,)
return
view
########## Resource Mixin ##########
########## Resource Mixin ##########
class
ResourceMixin
(
object
):
class
ResourceMixin
(
object
):
"""
Provides request validation and response filtering behavior.
"""
"""
Should be a class as described in the ``resources`` module.
The ``resource`` is an object that maps a view onto it's representation on the server.
It provides validation on the content of incoming requests,
and filters the object representation into a serializable object for the response.
"""
resource
=
None
@property
@property
def
CONTENT
(
self
):
def
CONTENT
(
self
):
if
not
hasattr
(
self
,
'_content'
):
if
not
hasattr
(
self
,
'_content'
):
self
.
_content
=
self
.
_get_content
(
)
self
.
_content
=
self
.
validate_request
(
self
.
DATA
,
self
.
FILES
)
return
self
.
_content
return
self
.
_content
def
_get_content
(
self
):
def
validate_request
(
self
,
data
,
files
):
"""
Given the request data return the cleaned, validated content.
Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
"""
resource
=
self
.
resource
(
self
)
resource
=
self
.
resource
(
self
)
return
resource
.
validate
(
self
.
DATA
,
self
.
FILES
)
return
resource
.
validate_request
(
data
,
files
)
def
filter_response
(
self
,
obj
):
"""
Given the response content, filter it into a serializable object.
"""
resource
=
self
.
resource
(
self
)
return
resource
.
filter_response
(
obj
)
def
get_bound_form
(
self
,
content
=
None
):
def
get_bound_form
(
self
,
content
=
None
):
resource
=
self
.
resource
(
self
)
resource
=
self
.
resource
(
self
)
return
resource
.
get_bound_form
(
content
)
return
resource
.
get_bound_form
(
content
)
def
object_to_data
(
self
,
obj
):
resource
=
self
.
resource
(
self
)
return
resource
.
object_to_data
(
obj
)
########## Model Mixins ##########
########## Model Mixins ##########
...
...
djangorestframework/resources.py
View file @
325e63a3
from
django
import
forms
from
django.core.urlresolvers
import
reverse
,
get_urlconf
,
get_resolver
,
NoReverseMatch
from
django.db
import
models
from
django.db
import
models
from
django.db.models.query
import
QuerySet
from
django.db.models.query
import
QuerySet
from
django.db.models.fields.related
import
RelatedField
from
django.db.models.fields.related
import
RelatedField
...
@@ -9,10 +11,15 @@ import re
...
@@ -9,10 +11,15 @@ import re
def
_model_to_dict
(
instance
,
fields
=
None
,
exclud
e
=
None
):
def
_model_to_dict
(
instance
,
resourc
e
=
None
):
"""
"""
This is a clone of Django's ``django.forms.model_to_dict`` except that it
Given a model instance, return a ``dict`` representing the model.
doesn't coerce related objects into primary keys.
The implementation is similar to Django's ``django.forms.model_to_dict``, except:
* It doesn't coerce related objects into primary keys.
* It doesn't drop ``editable=False`` fields.
* It also supports attribute or method fields on the instance or resource.
"""
"""
opts
=
instance
.
_meta
opts
=
instance
.
_meta
data
=
{}
data
=
{}
...
@@ -20,10 +27,19 @@ def _model_to_dict(instance, fields=None, exclude=None):
...
@@ -20,10 +27,19 @@ def _model_to_dict(instance, fields=None, exclude=None):
#print [rel.name for rel in opts.get_all_related_objects()]
#print [rel.name for rel in opts.get_all_related_objects()]
#related = [rel.get_accessor_name() for rel in opts.get_all_related_objects()]
#related = [rel.get_accessor_name() for rel in opts.get_all_related_objects()]
#print [getattr(instance, rel) for rel in related]
#print [getattr(instance, rel) for rel in related]
#if resource.fields:
# fields = resource.fields
#else:
# fields = set(opts.fields + opts.many_to_many)
fields
=
resource
.
fields
include
=
resource
.
include
exclude
=
resource
.
exclude
extra_fields
=
fields
and
list
(
resource
.
fields
)
or
[]
# Model fields
for
f
in
opts
.
fields
+
opts
.
many_to_many
:
for
f
in
opts
.
fields
+
opts
.
many_to_many
:
#if not f.editable:
# continue
if
fields
and
not
f
.
name
in
fields
:
if
fields
and
not
f
.
name
in
fields
:
continue
continue
if
exclude
and
f
.
name
in
exclude
:
if
exclude
and
f
.
name
in
exclude
:
...
@@ -33,86 +49,83 @@ def _model_to_dict(instance, fields=None, exclude=None):
...
@@ -33,86 +49,83 @@ def _model_to_dict(instance, fields=None, exclude=None):
else
:
else
:
data
[
f
.
name
]
=
f
.
value_from_object
(
instance
)
data
[
f
.
name
]
=
f
.
value_from_object
(
instance
)
#print fields - (opts.fields + opts.many_to_many)
if
extra_fields
and
f
.
name
in
extra_fields
:
#for related in [rel.get_accessor_name() for rel in opts.get_all_related_objects()]:
extra_fields
.
remove
(
f
.
name
)
# if fields and not related in fields:
# continue
# Method fields
# if exclude and related in exclude:
for
fname
in
extra_fields
:
# continue
if
hasattr
(
resource
,
fname
):
# data[related] = getattr(instance, related)
# check the resource first, to allow it to override fields
obj
=
getattr
(
resource
,
fname
)
# if it's a method like foo(self, instance), then call it
if
inspect
.
ismethod
(
obj
)
and
len
(
inspect
.
getargspec
(
obj
)[
0
])
==
2
:
obj
=
obj
(
instance
)
elif
hasattr
(
instance
,
fname
):
# now check the object instance
obj
=
getattr
(
instance
,
fname
)
else
:
continue
# TODO: It would be nicer if this didn't recurse here.
# Let's keep _model_to_dict flat, and _object_to_data recursive.
data
[
fname
]
=
_object_to_data
(
obj
)
return
data
return
data
def
_object_to_data
(
obj
):
def
_object_to_data
(
obj
,
resource
=
None
):
"""
"""
Convert an object into a serializable representation.
Convert an object into a serializable representation.
"""
"""
if
isinstance
(
obj
,
dict
):
if
isinstance
(
obj
,
dict
):
# dictionaries
# dictionaries
# TODO: apply same _model_to_dict logic fields/exclude here
return
dict
([
(
key
,
_object_to_data
(
val
))
for
key
,
val
in
obj
.
iteritems
()
])
return
dict
([
(
key
,
_object_to_data
(
val
))
for
key
,
val
in
obj
.
iteritems
()
])
if
isinstance
(
obj
,
(
tuple
,
list
,
set
,
QuerySet
)):
if
isinstance
(
obj
,
(
tuple
,
list
,
set
,
QuerySet
)):
# basic iterables
# basic iterables
return
[
_object_to_data
(
item
)
for
item
in
obj
]
return
[
_object_to_data
(
item
,
resource
)
for
item
in
obj
]
if
isinstance
(
obj
,
models
.
Manager
):
if
isinstance
(
obj
,
models
.
Manager
):
# Manager objects
# Manager objects
return
[
_object_to_data
(
item
)
for
item
in
obj
.
all
()]
return
[
_object_to_data
(
item
,
resource
)
for
item
in
obj
.
all
()]
if
isinstance
(
obj
,
models
.
Model
):
if
isinstance
(
obj
,
models
.
Model
):
# Model instances
# Model instances
return
_object_to_data
(
_model_to_dict
(
obj
))
return
_object_to_data
(
_model_to_dict
(
obj
,
resource
))
if
isinstance
(
obj
,
decimal
.
Decimal
):
if
isinstance
(
obj
,
decimal
.
Decimal
):
# Decimals (force to string representation)
# Decimals (force to string representation)
return
str
(
obj
)
return
str
(
obj
)
if
inspect
.
isfunction
(
obj
)
and
not
inspect
.
getargspec
(
obj
)[
0
]:
if
inspect
.
isfunction
(
obj
)
and
not
inspect
.
getargspec
(
obj
)[
0
]:
# function with no args
# function with no args
return
_object_to_data
(
obj
())
return
_object_to_data
(
obj
()
,
resource
)
if
inspect
.
ismethod
(
obj
)
and
len
(
inspect
.
getargspec
(
obj
)[
0
])
=
=
1
:
if
inspect
.
ismethod
(
obj
)
and
len
(
inspect
.
getargspec
(
obj
)[
0
])
<
=
1
:
#
method with only a 'self' args
#
bound method
return
_object_to_data
(
obj
())
return
_object_to_data
(
obj
()
,
resource
)
# fallback
return
smart_unicode
(
obj
,
strings_only
=
True
)
return
smart_unicode
(
obj
,
strings_only
=
True
)
def
_form_to_data
(
form
):
class
BaseResource
(
object
):
"""
"""
Returns a dict containing the data in a form instance.
Base class for all Resource classes, which simply defines the interface they provide.
This code is pretty much a clone of the ``Form.as_p()`` ``Form.as_ul``
and ``Form.as_table()`` methods, except that it returns data suitable
for arbitrary serialization, rather than rendering the result directly
into html.
"""
"""
ret
=
{}
fields
=
None
for
name
,
field
in
form
.
fields
.
items
():
include
=
None
if
not
form
.
is_bound
:
exclude
=
None
data
=
form
.
initial
.
get
(
name
,
field
.
initial
)
if
callable
(
data
):
data
=
data
()
else
:
if
isinstance
(
field
,
FileField
)
and
form
.
data
is
None
:
data
=
form
.
initial
.
get
(
name
,
field
.
initial
)
else
:
data
=
field
.
widget
.
value_from_datadict
(
form
.
data
,
form
.
files
,
name
)
ret
[
name
]
=
field
.
prepare_value
(
data
)
return
ret
class
BaseResource
(
object
):
"""Base class for all Resource classes, which simply defines the interface they provide."""
def
__init__
(
self
,
view
):
def
__init__
(
self
,
view
):
self
.
view
=
view
self
.
view
=
view
def
validate
(
self
,
data
,
files
):
def
validate_request
(
self
,
data
,
files
):
"""Given some content as input return some cleaned, validated content.
"""
Given the request data return the cleaned, validated content.
Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
"""
Must be overridden to be implemented."""
return
data
return
data
def
object_to_data
(
self
,
obj
):
def
filter_response
(
self
,
obj
):
return
_object_to_data
(
obj
)
"""
Given the response content, filter it into a serializable object.
"""
return
_object_to_data
(
obj
,
self
)
class
Resource
(
BaseResource
):
class
Resource
(
BaseResource
):
...
@@ -135,247 +148,18 @@ class Resource(BaseResource):
...
@@ -135,247 +148,18 @@ class Resource(BaseResource):
# you should explicitly set the fields attribute on your class.
# you should explicitly set the fields attribute on your class.
fields
=
None
fields
=
None
# TODO: Replace this with new Serializer code based on Forms API.
def
object_to_data
(
self
,
obj
):
"""
A (horrible) munging of Piston's pre-serialization. Returns a dict.
"""
return
_object_to_data
(
obj
)
def
_any
(
thing
,
fields
=
()):
"""
Dispatch, all types are routed through here.
"""
ret
=
None
if
isinstance
(
thing
,
QuerySet
):
ret
=
_qs
(
thing
,
fields
=
fields
)
elif
isinstance
(
thing
,
(
tuple
,
list
)):
ret
=
_list
(
thing
)
elif
isinstance
(
thing
,
dict
):
ret
=
_dict
(
thing
)
elif
isinstance
(
thing
,
int
):
ret
=
thing
elif
isinstance
(
thing
,
bool
):
ret
=
thing
elif
isinstance
(
thing
,
type
(
None
)):
ret
=
thing
elif
isinstance
(
thing
,
decimal
.
Decimal
):
ret
=
str
(
thing
)
elif
isinstance
(
thing
,
models
.
Model
):
ret
=
_model
(
thing
,
fields
=
fields
)
#elif isinstance(thing, HttpResponse): TRC
# raise HttpStatusCode(thing)
elif
inspect
.
isfunction
(
thing
):
if
not
inspect
.
getargspec
(
thing
)[
0
]:
ret
=
_any
(
thing
())
elif
hasattr
(
thing
,
'__rendertable__'
):
f
=
thing
.
__rendertable__
if
inspect
.
ismethod
(
f
)
and
len
(
inspect
.
getargspec
(
f
)[
0
])
==
1
:
ret
=
_any
(
f
())
else
:
ret
=
unicode
(
thing
)
# TRC
return
ret
def
_fk
(
data
,
field
):
"""
Foreign keys.
"""
return
_any
(
getattr
(
data
,
field
.
name
))
def
_related
(
data
,
fields
=
()):
"""
Foreign keys.
"""
return
[
_model
(
m
,
fields
)
for
m
in
data
.
iterator
()
]
def
_m2m
(
data
,
field
,
fields
=
()):
"""
Many to many (re-route to `_model`.)
"""
return
[
_model
(
m
,
fields
)
for
m
in
getattr
(
data
,
field
.
name
)
.
iterator
()
]
def
_method_fields
(
data
,
fields
):
if
not
data
:
return
{
}
has
=
dir
(
data
)
ret
=
dict
()
for
field
in
fields
:
if
field
in
has
:
ret
[
field
]
=
getattr
(
data
,
field
)
return
ret
def
_model
(
data
,
fields
=
()):
"""
Models. Will respect the `fields` and/or
`exclude` on the handler (see `typemapper`.)
"""
ret
=
{
}
#handler = self.in_typemapper(type(data), self.anonymous) # TRC
handler
=
None
# TRC
get_absolute_url
=
False
if
fields
:
v
=
lambda
f
:
getattr
(
data
,
f
.
attname
)
get_fields
=
set
(
fields
)
if
'absolute_url'
in
get_fields
:
# MOVED (TRC)
get_absolute_url
=
True
met_fields
=
_method_fields
(
handler
,
get_fields
)
# TRC
for
f
in
data
.
_meta
.
local_fields
:
if
f
.
serialize
and
not
any
([
p
in
met_fields
for
p
in
[
f
.
attname
,
f
.
name
]]):
if
not
f
.
rel
:
if
f
.
attname
in
get_fields
:
ret
[
f
.
attname
]
=
_any
(
v
(
f
))
get_fields
.
remove
(
f
.
attname
)
else
:
if
f
.
attname
[:
-
3
]
in
get_fields
:
ret
[
f
.
name
]
=
_fk
(
data
,
f
)
get_fields
.
remove
(
f
.
name
)
for
mf
in
data
.
_meta
.
many_to_many
:
if
mf
.
serialize
and
mf
.
attname
not
in
met_fields
:
if
mf
.
attname
in
get_fields
:
ret
[
mf
.
name
]
=
_m2m
(
data
,
mf
)
get_fields
.
remove
(
mf
.
name
)
# try to get the remainder of fields
for
maybe_field
in
get_fields
:
if
isinstance
(
maybe_field
,
(
list
,
tuple
)):
model
,
fields
=
maybe_field
inst
=
getattr
(
data
,
model
,
None
)
if
inst
:
if
hasattr
(
inst
,
'all'
):
ret
[
model
]
=
_related
(
inst
,
fields
)
elif
callable
(
inst
):
if
len
(
inspect
.
getargspec
(
inst
)[
0
])
==
1
:
ret
[
model
]
=
_any
(
inst
(),
fields
)
else
:
ret
[
model
]
=
_model
(
inst
,
fields
)
elif
maybe_field
in
met_fields
:
# Overriding normal field which has a "resource method"
# so you can alter the contents of certain fields without
# using different names.
ret
[
maybe_field
]
=
_any
(
met_fields
[
maybe_field
](
data
))
else
:
maybe
=
getattr
(
data
,
maybe_field
,
None
)
if
maybe
:
if
callable
(
maybe
):
if
len
(
inspect
.
getargspec
(
maybe
)[
0
])
==
1
:
ret
[
maybe_field
]
=
_any
(
maybe
())
else
:
ret
[
maybe_field
]
=
_any
(
maybe
)
else
:
pass
# TRC
#handler_f = getattr(handler or self.handler, maybe_field, None)
#
#if handler_f:
# ret[maybe_field] = _any(handler_f(data))
else
:
# Add absolute_url if it exists
get_absolute_url
=
True
# Add all the fields
for
f
in
data
.
_meta
.
fields
:
if
f
.
attname
!=
'id'
:
ret
[
f
.
attname
]
=
_any
(
getattr
(
data
,
f
.
attname
))
# Add all the propertiess
klass
=
data
.
__class__
for
attr
in
dir
(
klass
):
if
not
attr
.
startswith
(
'_'
)
and
not
attr
in
(
'pk'
,
'id'
)
and
isinstance
(
getattr
(
klass
,
attr
,
None
),
property
):
#if attr.endswith('_url') or attr.endswith('_uri'):
# ret[attr] = self.make_absolute(_any(getattr(data, attr)))
#else:
ret
[
attr
]
=
_any
(
getattr
(
data
,
attr
))
#fields = dir(data.__class__) + ret.keys()
#add_ons = [k for k in dir(data) if k not in fields and not k.startswith('_')]
#print add_ons
###print dir(data.__class__)
#from django.db.models import Model
#model_fields = dir(Model)
#for attr in dir(data):
## #if attr.startswith('_'):
## # continue
# if (attr in fields) and not (attr in model_fields) and not attr.startswith('_'):
# print attr, type(getattr(data, attr, None)), attr in fields, attr in model_fields
#for k in add_ons:
# ret[k] = _any(getattr(data, k))
# TRC
# resouce uri
#if self.in_typemapper(type(data), self.anonymous):
# handler = self.in_typemapper(type(data), self.anonymous)
# if hasattr(handler, 'resource_uri'):
# url_id, fields = handler.resource_uri()
# ret['resource_uri'] = permalink( lambda: (url_id,
# (getattr(data, f) for f in fields) ) )()
# TRC
#if hasattr(data, 'get_api_url') and 'resource_uri' not in ret:
# try: ret['resource_uri'] = data.get_api_url()
# except: pass
# absolute uri
if
hasattr
(
data
,
'get_absolute_url'
)
and
get_absolute_url
:
try
:
ret
[
'absolute_url'
]
=
data
.
get_absolute_url
()
except
:
pass
#for key, val in ret.items():
# if key.endswith('_url') or key.endswith('_uri'):
# ret[key] = self.add_domain(val)
return
ret
def
_qs
(
data
,
fields
=
()):
"""
Querysets.
"""
return
[
_any
(
v
,
fields
)
for
v
in
data
]
def
_list
(
data
):
"""
Lists.
"""
return
[
_any
(
v
)
for
v
in
data
]
def
_dict
(
data
):
"""
Dictionaries.
"""
return
dict
([
(
k
,
_any
(
v
))
for
k
,
v
in
data
.
iteritems
()
])
# Kickstart the seralizin'.
return
_any
(
obj
,
self
.
fields
)
class
FormResource
(
Resource
):
class
FormResource
(
Resource
):
"""Validator class that uses forms for validation.
"""
Resource class that uses forms for validation.
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.
The view class should provide `.form` attribute which specifies the form classmethod
to be used for validation.
On calling validate() this validator may set a `.bound_form_instance` attribute on the
On calling validate() this validator may set a `.bound_form_instance` attribute on the
view, which may be used by some renderers."""
view, which may be used by some renderers.
"""
form
=
None
def
validate
(
self
,
data
,
files
):
def
validate
_request
(
self
,
data
,
files
):
"""
"""
Given some content as input return some cleaned, validated content.
Given some content as input return some cleaned, validated content.
Raises a ErrorResponse with status code 400 (Bad Request) on failure.
Raises a ErrorResponse with status code 400 (Bad Request) on failure.
...
@@ -434,10 +218,12 @@ class FormResource(Resource):
...
@@ -434,10 +218,12 @@ class FormResource(Resource):
detail
[
u'errors'
]
=
bound_form
.
non_field_errors
()
detail
[
u'errors'
]
=
bound_form
.
non_field_errors
()
# Add standard field errors
# Add standard field errors
field_errors
=
dict
((
key
,
map
(
unicode
,
val
))
field_errors
=
dict
(
(
key
,
map
(
unicode
,
val
))
for
(
key
,
val
)
for
(
key
,
val
)
in
bound_form
.
errors
.
iteritems
()
in
bound_form
.
errors
.
iteritems
()
if
not
key
.
startswith
(
'__'
))
if
not
key
.
startswith
(
'__'
)
)
# Add any unknown field errors
# Add any unknown field errors
for
key
in
unknown_fields
:
for
key
in
unknown_fields
:
...
@@ -451,22 +237,24 @@ class FormResource(Resource):
...
@@ -451,22 +237,24 @@ class FormResource(Resource):
def
get_bound_form
(
self
,
data
=
None
,
files
=
None
):
def
get_bound_form
(
self
,
data
=
None
,
files
=
None
):
"""
Given some content return a Django form bound to that content.
"""
If form validation is turned off (form class attribute is None) then returns None."""
Given some content return a Django form bound to that content.
form_cls
=
getattr
(
self
,
'form'
,
None
)
If form validation is turned off (form class attribute is None) then returns None.
"""
if
not
form_cls
:
if
not
self
.
form
:
return
None
return
None
if
data
is
not
None
:
if
data
is
not
None
:
return
form_cls
(
data
,
files
)
return
self
.
form
(
data
,
files
)
return
form_cls
()
return
self
.
form
()
class
ModelResource
(
FormResource
):
class
ModelResource
(
FormResource
):
"""Validator 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."""
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.
"""
"""The form class that should be used for validation, or None to use model form validation."""
"""The form class that should be used for validation, or None to use model form validation."""
form
=
None
form
=
None
...
@@ -477,16 +265,16 @@ class ModelResource(FormResource):
...
@@ -477,16 +265,16 @@ class ModelResource(FormResource):
"""The list of fields we expect to receive as input. Fields in this list will may be received with
"""The list of fields we expect to receive as input. Fields in this list will may be received with
raising non-existent field errors, even if they do not exist as fields on the ModelForm.
raising non-existent field errors, even if they do not exist as fields on the ModelForm.
Setting the fields class attribute causes the exclude
_fields
class attribute to be disregarded."""
Setting the fields class attribute causes the exclude class attribute to be disregarded."""
fields
=
None
fields
=
None
"""The list of fields to exclude from the Model. This is only used if the fields class attribute is not set."""
"""The list of fields to exclude from the Model. This is only used if the fields class attribute is not set."""
exclude
_fields
=
(
'id'
,
'pk'
)
exclude
=
(
'id'
,
'pk'
)
# TODO: test the different validation here to allow for get get_absolute_url to be supplied on input and not bork out
# TODO: test the different validation here to allow for get get_absolute_url to be supplied on input and not bork out
# TODO: be really strict on fields - check they match in the handler methods. (this isn't a validator thing tho.)
# TODO: be really strict on fields - check they match in the handler methods. (this isn't a validator thing tho.)
def
validate
(
self
,
data
,
files
):
def
validate
_request
(
self
,
data
,
files
):
"""
"""
Given some content as input return some cleaned, validated content.
Given some content as input return some cleaned, validated content.
Raises a ErrorResponse with status code 400 (Bad Request) on failure.
Raises a ErrorResponse with status code 400 (Bad Request) on failure.
...
@@ -503,66 +291,80 @@ class ModelResource(FormResource):
...
@@ -503,66 +291,80 @@ 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
,
data
=
None
,
files
=
None
):
def
get_bound_form
(
self
,
content
=
None
):
"""Given some content return a Django form bound to that content.
"""Given some content return a Django form bound to that content.
If the form class attribute has been explicitly set then use that class to create a Form,
If the form class attribute has been explicitly set then use that class to create a Form,
otherwise if model is set use that class to create a ModelForm, otherwise return None."""
otherwise if model is set use that class to create a ModelForm, otherwise return None."""
form_cls
=
getattr
(
self
,
'form'
,
None
)
if
self
.
form
:
model_cls
=
getattr
(
self
,
'model'
,
None
)
if
form_cls
:
# Use explict Form
# Use explict Form
return
super
(
ModelFormValidator
,
self
)
.
get_bound_form
(
data
,
files
)
return
super
(
ModelFormValidator
,
self
)
.
get_bound_form
(
data
,
files
)
elif
model_cls
:
elif
self
.
model
:
# Fall back to ModelForm which we create on the fly
# Fall back to ModelForm which we create on the fly
class
OnTheFlyModelForm
(
forms
.
ModelForm
):
class
OnTheFlyModelForm
(
forms
.
ModelForm
):
class
Meta
:
class
Meta
:
model
=
model_cls
model
=
self
.
model
#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
content
and
isinstance
(
content
,
models
.
Model
):
# Bound to an existing model instance
# Bound to an existing model instance
return
OnTheFlyModelForm
(
instance
=
content
)
return
OnTheFlyModelForm
(
instance
=
content
)
elif
not
data
is
None
:
elif
content
is
not
None
:
return
OnTheFlyModelForm
(
data
,
files
)
return
OnTheFlyModelForm
(
content
)
return
OnTheFlyModelForm
()
return
OnTheFlyModelForm
()
# Both form and model not set? Okay bruv, whatevs...
# Both form and model not set? Okay bruv, whatevs...
return
None
return
None
def
url
(
self
,
instance
):
"""
Attempts to reverse resolve the url of the given model instance for this resource.
"""
# dis does teh magicks...
urlconf
=
get_urlconf
()
resolver
=
get_resolver
(
urlconf
)
possibilities
=
resolver
.
reverse_dict
.
getlist
(
self
.
view_callable
[
0
])
for
tuple_item
in
possibilities
:
possibility
=
tuple_item
[
0
]
# pattern = tuple_item[1]
# Note: defaults = tuple_item[2] for django >= 1.3
for
result
,
params
in
possibility
:
instance_attrs
=
dict
([
(
param
,
getattr
(
instance
,
param
))
for
param
in
params
if
hasattr
(
instance
,
param
)
])
try
:
return
reverse
(
self
.
view_callable
[
0
],
kwargs
=
instance_attrs
)
except
NoReverseMatch
:
pass
raise
NoReverseMatch
@property
@property
def
_model_fields_set
(
self
):
def
_model_fields_set
(
self
):
"""Return a set containing the names of validated fields on the model."""
"""
resource
=
self
.
view
.
resource
Return a set containing the names of validated fields on the model.
model
=
getattr
(
resource
,
'model'
,
None
)
"""
fields
=
getattr
(
resource
,
'fields'
,
self
.
fields
)
model_fields
=
set
(
field
.
name
for
field
in
self
.
model
.
_meta
.
fields
)
exclude_fields
=
getattr
(
resource
,
'exclude_fields'
,
self
.
exclude_fields
)
model_fields
=
set
(
field
.
name
for
field
in
model
.
_meta
.
fields
)
if
fields
:
if
fields
:
return
model_fields
&
set
(
as_tuple
(
fields
))
return
model_fields
&
set
(
as_tuple
(
self
.
fields
))
return
model_fields
-
set
(
as_tuple
(
exclude_fields
))
return
model_fields
-
set
(
as_tuple
(
self
.
exclude
))
@property
@property
def
_property_fields_set
(
self
):
def
_property_fields_set
(
self
):
"""Returns a set containing the names of validated properties on the model."""
"""
resource
=
self
.
view
.
resource
Returns a set containing the names of validated properties on the model.
model
=
getattr
(
resource
,
'model'
,
None
)
"""
fields
=
getattr
(
resource
,
'fields'
,
self
.
fields
)
property_fields
=
set
(
attr
for
attr
in
dir
(
self
.
model
)
if
exclude_fields
=
getattr
(
resource
,
'exclude_fields'
,
self
.
exclude_fields
)
isinstance
(
getattr
(
self
.
model
,
attr
,
None
),
property
)
property_fields
=
set
(
attr
for
attr
in
dir
(
model
)
if
isinstance
(
getattr
(
model
,
attr
,
None
),
property
)
and
not
attr
.
startswith
(
'_'
))
and
not
attr
.
startswith
(
'_'
))
if
fields
:
if
fields
:
return
property_fields
&
set
(
as_tuple
(
fields
))
return
property_fields
&
set
(
as_tuple
(
self
.
fields
))
return
property_fields
-
set
(
as_tuple
(
exclude_fields
))
return
property_fields
-
set
(
as_tuple
(
self
.
exclude
))
djangorestframework/tests/resources.py
View file @
325e63a3
djangorestframework/utils/mediatypes.py
View file @
325e63a3
...
@@ -43,6 +43,7 @@ def add_media_type_param(media_type, key, val):
...
@@ -43,6 +43,7 @@ def add_media_type_param(media_type, key, val):
media_type
.
params
[
key
]
=
val
media_type
.
params
[
key
]
=
val
return
str
(
media_type
)
return
str
(
media_type
)
def
get_media_type_params
(
media_type
):
def
get_media_type_params
(
media_type
):
"""
"""
Return a dictionary of the parameters on the given media type.
Return a dictionary of the parameters on the given media type.
...
...
djangorestframework/views.py
View file @
325e63a3
...
@@ -18,8 +18,10 @@ __all__ = (
...
@@ -18,8 +18,10 @@ __all__ = (
class
BaseView
(
ResourceMixin
,
RequestMixin
,
ResponseMixin
,
AuthMixin
,
View
):
class
BaseView
(
ResourceMixin
,
RequestMixin
,
ResponseMixin
,
AuthMixin
,
View
):
"""Handles incoming requests and maps them to REST operations.
"""
Performs request deserialization, response serialization, authentication and input validation."""
Handles incoming requests and maps them to REST operations.
Performs request deserialization, response serialization, authentication and input validation.
"""
# Use the base resource by default
# Use the base resource by default
resource
=
resources
.
Resource
resource
=
resources
.
Resource
...
@@ -78,7 +80,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -78,7 +80,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
set_script_prefix
(
prefix
)
set_script_prefix
(
prefix
)
try
:
try
:
# Authenticate and check request
is
has the relevant permissions
# Authenticate and check request has the relevant permissions
self
.
_check_permissions
()
self
.
_check_permissions
()
# Get the appropriate handler method
# Get the appropriate handler method
...
@@ -98,7 +100,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -98,7 +100,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
response
=
Response
(
status
.
HTTP_204_NO_CONTENT
)
response
=
Response
(
status
.
HTTP_204_NO_CONTENT
)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
response
.
cleaned_content
=
self
.
object_to_data
(
response
.
raw_content
)
response
.
cleaned_content
=
self
.
filter_response
(
response
.
raw_content
)
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
response
=
exc
.
response
response
=
exc
.
response
...
@@ -118,7 +120,7 @@ class ModelView(BaseView):
...
@@ -118,7 +120,7 @@ class ModelView(BaseView):
"""A RESTful view that maps to a model in the database."""
"""A RESTful view that maps to a model in the database."""
resource
=
resources
.
ModelResource
resource
=
resources
.
ModelResource
class
InstanceModelView
(
ReadModelMixin
,
UpdateModelMixin
,
DeleteModelMixin
,
ModelView
):
class
InstanceModelView
(
InstanceMixin
,
ReadModelMixin
,
UpdateModelMixin
,
DeleteModelMixin
,
ModelView
):
"""A view which provides default operations for read/update/delete against a model instance."""
"""A view which provides default operations for read/update/delete against a model instance."""
pass
pass
...
...
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