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
42b3fdbd
Commit
42b3fdbd
authored
Oct 04, 2012
by
Tom Christie
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #279 from tomchristie/hyperlinked-relationships
Hyperlinked relationships
parents
ad214976
eaebb397
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
273 additions
and
146 deletions
+273
-146
rest_framework/fields.py
+158
-129
rest_framework/serializers.py
+54
-15
rest_framework/settings.py
+1
-1
rest_framework/tests/generics.py
+0
-1
rest_framework/tests/hyperlinkedserializers.py
+60
-0
No files found.
rest_framework/fields.py
View file @
42b3fdbd
...
@@ -9,6 +9,7 @@ from django.conf import settings
...
@@ -9,6 +9,7 @@ from django.conf import settings
from
django.db
import
DEFAULT_DB_ALIAS
from
django.db
import
DEFAULT_DB_ALIAS
from
django.utils.encoding
import
is_protected_type
,
smart_unicode
from
django.utils.encoding
import
is_protected_type
,
smart_unicode
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
rest_framework.reverse
import
reverse
from
rest_framework.compat
import
parse_date
,
parse_datetime
from
rest_framework.compat
import
parse_date
,
parse_datetime
from
rest_framework.compat
import
timezone
from
rest_framework.compat
import
timezone
...
@@ -25,21 +26,88 @@ def is_simple_callable(obj):
...
@@ -25,21 +26,88 @@ def is_simple_callable(obj):
class
Field
(
object
):
class
Field
(
object
):
creation_counter
=
0
creation_counter
=
0
default_validators
=
[]
default_error_messages
=
{
'required'
:
_
(
'This field is required.'
),
'invalid'
:
_
(
'Invalid value.'
),
}
empty
=
''
empty
=
''
def
__init__
(
self
,
source
=
None
,
readonly
=
False
,
required
=
None
,
def
__init__
(
self
,
source
=
None
):
validators
=
[],
error_messages
=
None
):
self
.
parent
=
None
self
.
parent
=
None
self
.
creation_counter
=
Field
.
creation_counter
self
.
creation_counter
=
Field
.
creation_counter
Field
.
creation_counter
+=
1
Field
.
creation_counter
+=
1
self
.
source
=
source
self
.
source
=
source
def
initialize
(
self
,
parent
):
"""
Called to set up a field prior to field_to_native or field_from_native.
parent - The parent serializer.
model_field - The model field this field corrosponds to, if one exists.
"""
self
.
parent
=
parent
self
.
root
=
parent
.
root
or
parent
self
.
context
=
self
.
root
.
context
def
field_from_native
(
self
,
data
,
field_name
,
into
):
"""
Given a dictionary and a field name, updates the dictionary `into`,
with the field and it's deserialized value.
"""
return
def
field_to_native
(
self
,
obj
,
field_name
):
"""
Given and object and a field name, returns the value that should be
serialized for that field.
"""
if
obj
is
None
:
return
self
.
empty
if
self
.
source
==
'*'
:
return
self
.
to_native
(
obj
)
if
self
.
source
:
value
=
obj
for
component
in
self
.
source
.
split
(
'.'
):
value
=
getattr
(
value
,
component
)
else
:
value
=
getattr
(
obj
,
field_name
)
return
self
.
to_native
(
value
)
def
to_native
(
self
,
value
):
"""
Converts the field's value into it's simple representation.
"""
if
is_simple_callable
(
value
):
value
=
value
()
if
is_protected_type
(
value
):
return
value
elif
hasattr
(
value
,
'__iter__'
)
and
not
isinstance
(
value
,
(
dict
,
basestring
)):
return
[
self
.
to_native
(
item
)
for
item
in
value
]
return
smart_unicode
(
value
)
def
attributes
(
self
):
"""
Returns a dictionary of attributes to be used when serializing to xml.
"""
if
getattr
(
self
,
'type_name'
,
None
):
return
{
'type'
:
self
.
type_name
}
return
{}
class
WritableField
(
Field
):
"""
Base for read/write fields.
"""
default_validators
=
[]
default_error_messages
=
{
'required'
:
_
(
'This field is required.'
),
'invalid'
:
_
(
'Invalid value.'
),
}
def
__init__
(
self
,
source
=
None
,
readonly
=
False
,
required
=
None
,
validators
=
[],
error_messages
=
None
):
super
(
WritableField
,
self
)
.
__init__
(
source
=
source
)
self
.
readonly
=
readonly
self
.
readonly
=
readonly
if
required
is
None
:
if
required
is
None
:
self
.
required
=
not
(
readonly
)
self
.
required
=
not
(
readonly
)
...
@@ -55,19 +123,6 @@ class Field(object):
...
@@ -55,19 +123,6 @@ class Field(object):
self
.
validators
=
self
.
default_validators
+
validators
self
.
validators
=
self
.
default_validators
+
validators
def
initialize
(
self
,
parent
,
model_field
=
None
):
"""
Called to set up a field prior to field_to_native or field_from_native.
parent - The parent serializer.
model_field - The model field this field corrosponds to, if one exists.
"""
self
.
parent
=
parent
self
.
root
=
parent
.
root
or
parent
self
.
context
=
self
.
root
.
context
if
model_field
:
self
.
model_field
=
model_field
def
validate
(
self
,
value
):
def
validate
(
self
,
value
):
if
value
in
validators
.
EMPTY_VALUES
and
self
.
required
:
if
value
in
validators
.
EMPTY_VALUES
and
self
.
required
:
raise
ValidationError
(
self
.
error_messages
[
'required'
])
raise
ValidationError
(
self
.
error_messages
[
'required'
])
...
@@ -116,88 +171,75 @@ class Field(object):
...
@@ -116,88 +171,75 @@ class Field(object):
"""
"""
Reverts a simple representation back to the field's value.
Reverts a simple representation back to the field's value.
"""
"""
if
hasattr
(
self
,
'model_field'
):
try
:
return
self
.
model_field
.
rel
.
to
.
_meta
.
get_field
(
self
.
model_field
.
rel
.
field_name
)
.
to_python
(
value
)
except
:
return
self
.
model_field
.
to_python
(
value
)
return
value
return
value
def
field_to_native
(
self
,
obj
,
field_name
):
"""
Given and object and a field name, returns the value that should be
serialized for that field.
"""
if
obj
is
None
:
return
self
.
empty
if
self
.
source
==
'*'
:
return
self
.
to_native
(
obj
)
self
.
obj
=
obj
# Need to hang onto this in the case of model fields
if
hasattr
(
self
,
'model_field'
):
return
self
.
to_native
(
self
.
model_field
.
_get_val_from_obj
(
obj
))
if
self
.
source
:
class
ModelField
(
WritableField
):
value
=
obj
"""
for
component
in
self
.
source
.
split
(
'.'
):
A generic field that can be used against an arbirtrary model field.
value
=
getattr
(
value
,
component
)
"""
else
:
def
__init__
(
self
,
*
args
,
**
kwargs
):
value
=
getattr
(
obj
,
field_name
)
try
:
return
self
.
to_native
(
value
)
self
.
model_field
=
kwargs
.
pop
(
'model_field'
)
except
:
raise
ValueError
(
"ModelField requires 'model_field' kwarg"
)
super
(
ModelField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
to
_native
(
self
,
value
):
def
from
_native
(
self
,
value
):
"""
try
:
Converts the field's value into it's simple representation.
rel
=
self
.
model_field
.
rel
"""
except
:
if
is_simple_callable
(
value
):
return
self
.
model_field
.
to_python
(
value
)
value
=
value
(
)
return
rel
.
to
.
_meta
.
get_field
(
rel
.
field_name
)
.
to_python
(
value
)
def
field_to_native
(
self
,
obj
,
field_name
):
value
=
self
.
model_field
.
_get_val_from_obj
(
obj
)
if
is_protected_type
(
value
):
if
is_protected_type
(
value
):
return
value
return
value
elif
hasattr
(
self
,
'model_field'
):
return
self
.
model_field
.
value_to_string
(
self
.
obj
)
return
self
.
model_field
.
value_to_string
(
self
.
obj
)
elif
hasattr
(
value
,
'__iter__'
)
and
not
isinstance
(
value
,
(
dict
,
basestring
)):
return
[
self
.
to_native
(
item
)
for
item
in
value
]
return
smart_unicode
(
value
)
def
attributes
(
self
):
def
attributes
(
self
):
"""
return
{
Returns a dictionary of attributes to be used when serializing to xml.
"type"
:
self
.
model_field
.
get_internal_type
()
"""
}
try
:
return
{
"type"
:
self
.
model_field
.
get_internal_type
()
}
except
AttributeError
:
return
{}
##### Relational fields #####
class
RelatedField
(
Field
):
"""
A base class for model related fields or related managers.
Subclass this and override `convert` to define custom behaviour when
class
RelatedField
(
WritableField
):
serializing related objects.
"""
Base class for related model fields.
"""
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
queryset
=
kwargs
.
pop
(
'queryset'
,
None
)
self
.
queryset
=
kwargs
.
pop
(
'queryset'
,
None
)
super
(
RelatedField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
RelatedField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
field_to_native
(
self
,
obj
,
field_name
):
def
field_to_native
(
self
,
obj
,
field_name
):
obj
=
getattr
(
obj
,
self
.
source
or
field_name
)
value
=
getattr
(
obj
,
self
.
source
or
field_name
)
if
obj
.
__class__
.
__name__
in
(
'RelatedManager'
,
'ManyRelatedManager'
):
return
self
.
to_native
(
value
)
return
[
self
.
to_native
(
item
)
for
item
in
obj
.
all
()]
return
self
.
to_native
(
obj
)
def
attributes
(
self
):
def
field_from_native
(
self
,
data
,
field_name
,
into
):
value
=
data
.
get
(
field_name
)
into
[(
self
.
source
or
field_name
)
+
'_id'
]
=
self
.
from_native
(
value
)
class
ManyRelatedField
(
RelatedField
):
"""
Base class for related model managers.
"""
def
field_to_native
(
self
,
obj
,
field_name
):
value
=
getattr
(
obj
,
self
.
source
or
field_name
)
return
[
self
.
to_native
(
item
)
for
item
in
value
.
all
()]
def
field_from_native
(
self
,
data
,
field_name
,
into
):
try
:
try
:
return
{
value
=
data
.
getlist
(
self
.
source
or
field_name
)
"rel"
:
self
.
model_field
.
rel
.
__class__
.
__name__
,
except
:
"to"
:
smart_unicode
(
self
.
model_field
.
rel
.
to
.
_meta
)
value
=
data
.
get
(
self
.
source
or
field_name
)
}
else
:
except
AttributeError
:
if
value
==
[
''
]:
return
{}
value
=
[]
into
[
field_name
]
=
[
self
.
from_native
(
item
)
for
item
in
value
]
class
PrimaryKeyRelatedField
(
RelatedField
):
class
PrimaryKeyRelatedField
(
RelatedField
):
...
@@ -206,20 +248,11 @@ class PrimaryKeyRelatedField(RelatedField):
...
@@ -206,20 +248,11 @@ class PrimaryKeyRelatedField(RelatedField):
"""
"""
def
to_native
(
self
,
pk
):
def
to_native
(
self
,
pk
):
"""
You can subclass this method to provide different serialization
behavior based on the pk.
"""
return
pk
return
pk
def
field_to_native
(
self
,
obj
,
field_name
):
def
field_to_native
(
self
,
obj
,
field_name
):
# This is only implemented for performance reasons
#
# We could leave the default `RelatedField.field_to_native()` in place,
# and inside just implement `to_native()` as `return obj.pk`
#
# That would involve an extra database lookup.
try
:
try
:
# Prefer obj.serializable_value for performance reasons
pk
=
obj
.
serializable_value
(
self
.
source
or
field_name
)
pk
=
obj
.
serializable_value
(
self
.
source
or
field_name
)
except
AttributeError
:
except
AttributeError
:
# RelatedObject (reverse relationship)
# RelatedObject (reverse relationship)
...
@@ -228,18 +261,17 @@ class PrimaryKeyRelatedField(RelatedField):
...
@@ -228,18 +261,17 @@ class PrimaryKeyRelatedField(RelatedField):
# Forward relationship
# Forward relationship
return
self
.
to_native
(
pk
)
return
self
.
to_native
(
pk
)
def
field_from_native
(
self
,
data
,
field_name
,
into
):
value
=
data
.
get
(
field_name
)
into
[
field_name
+
'_id'
]
=
self
.
from_native
(
value
)
class
ManyPrimaryKeyRelatedField
(
ManyRelatedField
):
class
ManyPrimaryKeyRelatedField
(
PrimaryKeyRelatedField
):
"""
"""
Serializes a to-many related field or related manager to a pk value.
Serializes a to-many related field or related manager to a pk value.
"""
"""
def
to_native
(
self
,
pk
):
return
pk
def
field_to_native
(
self
,
obj
,
field_name
):
def
field_to_native
(
self
,
obj
,
field_name
):
try
:
try
:
# Prefer obj.serializable_value for performance reasons
queryset
=
obj
.
serializable_value
(
self
.
source
or
field_name
)
queryset
=
obj
.
serializable_value
(
self
.
source
or
field_name
)
except
AttributeError
:
except
AttributeError
:
# RelatedManager (reverse relationship)
# RelatedManager (reverse relationship)
...
@@ -248,40 +280,25 @@ class ManyPrimaryKeyRelatedField(PrimaryKeyRelatedField):
...
@@ -248,40 +280,25 @@ class ManyPrimaryKeyRelatedField(PrimaryKeyRelatedField):
# Forward relationship
# Forward relationship
return
[
self
.
to_native
(
item
.
pk
)
for
item
in
queryset
.
all
()]
return
[
self
.
to_native
(
item
.
pk
)
for
item
in
queryset
.
all
()]
def
field_from_native
(
self
,
data
,
field_name
,
into
):
try
:
value
=
data
.
getlist
(
field_name
)
except
:
value
=
data
.
get
(
field_name
)
else
:
if
value
==
[
''
]:
value
=
[]
into
[
field_name
]
=
[
self
.
from_native
(
item
)
for
item
in
value
]
class
NaturalKeyRelatedField
(
Related
Field
):
class
HyperlinkedIdentityField
(
Field
):
"""
"""
Serializes a model related field or related manager to a natural key value
.
A field that represents the model's identity using a hyperlink
.
"""
"""
is_natural_key
=
True
# XML renderer handles these differently
def
__init__
(
self
,
*
args
,
**
kwargs
):
pass
def
to_native
(
self
,
obj
):
if
hasattr
(
obj
,
'natural_key'
):
return
obj
.
natural_key
()
return
obj
def
field_from_native
(
self
,
data
,
field_name
,
into
):
def
field_to_native
(
self
,
obj
,
field_name
):
value
=
data
.
get
(
field_name
)
request
=
self
.
context
.
get
(
'request'
,
None
)
into
[
self
.
model_field
.
attname
]
=
self
.
from_native
(
value
)
view_name
=
self
.
parent
.
opts
.
view_name
view_kwargs
=
{
'pk'
:
obj
.
pk
}
return
reverse
(
view_name
,
kwargs
=
view_kwargs
,
request
=
request
)
def
from_native
(
self
,
value
):
# TODO: Support 'using' : db = options.pop('using', DEFAULT_DB_ALIAS)
manager
=
self
.
model_field
.
rel
.
to
.
_default_manager
manager
=
manager
.
db_manager
(
DEFAULT_DB_ALIAS
)
return
manager
.
get_by_natural_key
(
*
value
)
.
pk
##### Typed Fields #####
class
BooleanField
(
Field
):
class
BooleanField
(
WritableField
):
type_name
=
'BooleanField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
u"'
%
s' value must be either True or False."
),
'invalid'
:
_
(
u"'
%
s' value must be either True or False."
),
}
}
...
@@ -298,7 +315,9 @@ class BooleanField(Field):
...
@@ -298,7 +315,9 @@ class BooleanField(Field):
raise
ValidationError
(
self
.
error_messages
[
'invalid'
]
%
value
)
raise
ValidationError
(
self
.
error_messages
[
'invalid'
]
%
value
)
class
CharField
(
Field
):
class
CharField
(
WritableField
):
type_name
=
'CharField'
def
__init__
(
self
,
max_length
=
None
,
min_length
=
None
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
max_length
=
None
,
min_length
=
None
,
*
args
,
**
kwargs
):
self
.
max_length
,
self
.
min_length
=
max_length
,
min_length
self
.
max_length
,
self
.
min_length
=
max_length
,
min_length
super
(
CharField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
CharField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
...
@@ -314,6 +333,8 @@ class CharField(Field):
...
@@ -314,6 +333,8 @@ class CharField(Field):
class
EmailField
(
CharField
):
class
EmailField
(
CharField
):
type_name
=
'EmailField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
'Enter a valid e-mail address.'
),
'invalid'
:
_
(
'Enter a valid e-mail address.'
),
}
}
...
@@ -330,7 +351,9 @@ class EmailField(CharField):
...
@@ -330,7 +351,9 @@ class EmailField(CharField):
return
result
return
result
class
DateField
(
Field
):
class
DateField
(
WritableField
):
type_name
=
'DateField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
u"'
%
s' value has an invalid date format. It must be "
'invalid'
:
_
(
u"'
%
s' value has an invalid date format. It must be "
u"in YYYY-MM-DD format."
),
u"in YYYY-MM-DD format."
),
...
@@ -364,7 +387,9 @@ class DateField(Field):
...
@@ -364,7 +387,9 @@ class DateField(Field):
raise
ValidationError
(
msg
)
raise
ValidationError
(
msg
)
class
DateTimeField
(
Field
):
class
DateTimeField
(
WritableField
):
type_name
=
'DateTimeField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
u"'
%
s' value has an invalid format. It must be in "
'invalid'
:
_
(
u"'
%
s' value has an invalid format. It must be in "
u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."
),
u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."
),
...
@@ -415,7 +440,9 @@ class DateTimeField(Field):
...
@@ -415,7 +440,9 @@ class DateTimeField(Field):
raise
ValidationError
(
msg
)
raise
ValidationError
(
msg
)
class
IntegerField
(
Field
):
class
IntegerField
(
WritableField
):
type_name
=
'IntegerField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
'Enter a whole number.'
),
'invalid'
:
_
(
'Enter a whole number.'
),
'max_value'
:
_
(
'Ensure this value is less than or equal to
%(limit_value)
s.'
),
'max_value'
:
_
(
'Ensure this value is less than or equal to
%(limit_value)
s.'
),
...
@@ -441,7 +468,9 @@ class IntegerField(Field):
...
@@ -441,7 +468,9 @@ class IntegerField(Field):
return
value
return
value
class
FloatField
(
Field
):
class
FloatField
(
WritableField
):
type_name
=
'FloatField'
default_error_messages
=
{
default_error_messages
=
{
'invalid'
:
_
(
"'
%
s' value must be a float."
),
'invalid'
:
_
(
"'
%
s' value must be a float."
),
}
}
...
...
rest_framework/serializers.py
View file @
42b3fdbd
...
@@ -123,16 +123,8 @@ class BaseSerializer(Field):
...
@@ -123,16 +123,8 @@ class BaseSerializer(Field):
# Get the explicitly declared fields
# Get the explicitly declared fields
for
key
,
field
in
self
.
fields
.
items
():
for
key
,
field
in
self
.
fields
.
items
():
ret
[
key
]
=
field
ret
[
key
]
=
field
# Determine if the declared field corrosponds to a model field.
try
:
if
key
==
'pk'
:
model_field
=
obj
.
_meta
.
pk
else
:
model_field
=
obj
.
_meta
.
get_field_by_name
(
key
)[
0
]
except
:
model_field
=
None
# Set up the field
# Set up the field
field
.
initialize
(
parent
=
self
,
model_field
=
model_field
)
field
.
initialize
(
parent
=
self
)
# Add in the default fields
# Add in the default fields
fields
=
self
.
default_fields
(
serialize
,
obj
,
data
,
nested
)
fields
=
self
.
default_fields
(
serialize
,
obj
,
data
,
nested
)
...
@@ -157,12 +149,12 @@ class BaseSerializer(Field):
...
@@ -157,12 +149,12 @@ class BaseSerializer(Field):
#####
#####
# Field methods - used when the serializer class is itself used as a field.
# Field methods - used when the serializer class is itself used as a field.
def
initialize
(
self
,
parent
,
model_field
=
None
):
def
initialize
(
self
,
parent
):
"""
"""
Same behaviour as usual Field, except that we need to keep track
Same behaviour as usual Field, except that we need to keep track
of state so that we can deal with handling maximum depth and recursion.
of state so that we can deal with handling maximum depth and recursion.
"""
"""
super
(
BaseSerializer
,
self
)
.
initialize
(
parent
,
model_field
)
super
(
BaseSerializer
,
self
)
.
initialize
(
parent
)
self
.
stack
=
parent
.
stack
[:]
self
.
stack
=
parent
.
stack
[:]
if
parent
.
opts
.
nested
and
not
isinstance
(
parent
.
opts
.
nested
,
bool
):
if
parent
.
opts
.
nested
and
not
isinstance
(
parent
.
opts
.
nested
,
bool
):
self
.
opts
.
nested
=
parent
.
opts
.
nested
-
1
self
.
opts
.
nested
=
parent
.
opts
.
nested
-
1
...
@@ -296,12 +288,22 @@ class ModelSerializerOptions(SerializerOptions):
...
@@ -296,12 +288,22 @@ class ModelSerializerOptions(SerializerOptions):
self
.
model
=
getattr
(
meta
,
'model'
,
None
)
self
.
model
=
getattr
(
meta
,
'model'
,
None
)
class
ModelSerializer
(
RelatedField
,
Serializer
):
class
ModelSerializer
(
Serializer
):
"""
"""
A serializer that deals with model instances and querysets.
A serializer that deals with model instances and querysets.
"""
"""
_options_class
=
ModelSerializerOptions
_options_class
=
ModelSerializerOptions
def
field_to_native
(
self
,
obj
,
field_name
):
"""
Override default so that we can apply ModelSerializer as a nested
field to relationships.
"""
obj
=
getattr
(
obj
,
self
.
source
or
field_name
)
if
obj
.
__class__
.
__name__
in
(
'RelatedManager'
,
'ManyRelatedManager'
):
return
[
self
.
to_native
(
item
)
for
item
in
obj
.
all
()]
return
self
.
to_native
(
obj
)
def
default_fields
(
self
,
serialize
,
obj
=
None
,
data
=
None
,
nested
=
False
):
def
default_fields
(
self
,
serialize
,
obj
=
None
,
data
=
None
,
nested
=
False
):
"""
"""
Return all the fields that should be serialized for the model.
Return all the fields that should be serialized for the model.
...
@@ -330,7 +332,7 @@ class ModelSerializer(RelatedField, Serializer):
...
@@ -330,7 +332,7 @@ class ModelSerializer(RelatedField, Serializer):
field
=
self
.
get_field
(
model_field
)
field
=
self
.
get_field
(
model_field
)
if
field
:
if
field
:
field
.
initialize
(
parent
=
self
,
model_field
=
model_field
)
field
.
initialize
(
parent
=
self
)
ret
[
model_field
.
name
]
=
field
ret
[
model_field
.
name
]
=
field
return
ret
return
ret
...
@@ -339,7 +341,7 @@ class ModelSerializer(RelatedField, Serializer):
...
@@ -339,7 +341,7 @@ class ModelSerializer(RelatedField, Serializer):
"""
"""
Returns a default instance of the pk field.
Returns a default instance of the pk field.
"""
"""
return
Field
(
readonly
=
True
)
return
Field
()
def
get_nested_field
(
self
,
model_field
):
def
get_nested_field
(
self
,
model_field
):
"""
"""
...
@@ -373,7 +375,7 @@ class ModelSerializer(RelatedField, Serializer):
...
@@ -373,7 +375,7 @@ class ModelSerializer(RelatedField, Serializer):
try
:
try
:
return
field_mapping
[
model_field
.
__class__
]()
return
field_mapping
[
model_field
.
__class__
]()
except
KeyError
:
except
KeyError
:
return
Field
(
)
return
ModelField
(
model_field
=
model_field
)
def
restore_object
(
self
,
attrs
,
instance
=
None
):
def
restore_object
(
self
,
attrs
,
instance
=
None
):
"""
"""
...
@@ -396,3 +398,40 @@ class ModelSerializer(RelatedField, Serializer):
...
@@ -396,3 +398,40 @@ class ModelSerializer(RelatedField, Serializer):
"""
"""
self
.
object
.
save
()
self
.
object
.
save
()
return
self
.
object
.
object
return
self
.
object
.
object
class
HyperlinkedModelSerializerOptions
(
ModelSerializerOptions
):
"""
Options for HyperlinkedModelSerializer
"""
def
__init__
(
self
,
meta
):
super
(
HyperlinkedModelSerializerOptions
,
self
)
.
__init__
(
meta
)
self
.
view_name
=
getattr
(
meta
,
'view_name'
,
None
)
class
HyperlinkedModelSerializer
(
ModelSerializer
):
"""
"""
_options_class
=
HyperlinkedModelSerializerOptions
_default_view_name
=
'
%(model_name)
s-detail'
url
=
HyperlinkedIdentityField
()
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
HyperlinkedModelSerializer
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
if
self
.
opts
.
view_name
is
None
:
self
.
opts
.
view_name
=
self
.
_get_default_view_name
()
def
_get_default_view_name
(
self
):
"""
Return the view name to use if 'view_name' is not specified in 'Meta'
"""
model_meta
=
self
.
opts
.
model
.
_meta
format_kwargs
=
{
'app_label'
:
model_meta
.
app_label
,
'model_name'
:
model_meta
.
object_name
.
lower
()
}
return
self
.
_default_view_name
%
format_kwargs
def
get_pk_field
(
self
,
model_field
):
return
None
rest_framework/settings.py
View file @
42b3fdbd
...
@@ -46,7 +46,7 @@ DEFAULTS = {
...
@@ -46,7 +46,7 @@ DEFAULTS = {
'MODEL_SERIALIZER'
:
'rest_framework.serializers.ModelSerializer'
,
'MODEL_SERIALIZER'
:
'rest_framework.serializers.ModelSerializer'
,
'PAGINATION_SERIALIZER'
:
'rest_framework.pagination.PaginationSerializer'
,
'PAGINATION_SERIALIZER'
:
'rest_framework.pagination.PaginationSerializer'
,
'PAGINATE_BY'
:
20
,
'PAGINATE_BY'
:
None
,
'UNAUTHENTICATED_USER'
:
'django.contrib.auth.models.AnonymousUser'
,
'UNAUTHENTICATED_USER'
:
'django.contrib.auth.models.AnonymousUser'
,
'UNAUTHENTICATED_TOKEN'
:
None
,
'UNAUTHENTICATED_TOKEN'
:
None
,
...
...
rest_framework/tests/generics.py
View file @
42b3fdbd
...
@@ -13,7 +13,6 @@ class RootView(generics.ListCreateAPIView):
...
@@ -13,7 +13,6 @@ class RootView(generics.ListCreateAPIView):
Example description for OPTIONS.
Example description for OPTIONS.
"""
"""
model
=
BasicModel
model
=
BasicModel
paginate_by
=
None
class
InstanceView
(
generics
.
RetrieveUpdateDestroyAPIView
):
class
InstanceView
(
generics
.
RetrieveUpdateDestroyAPIView
):
...
...
rest_framework/tests/hyperlinkedserializers.py
0 → 100644
View file @
42b3fdbd
from
django.conf.urls.defaults
import
patterns
,
url
from
django.test
import
TestCase
from
django.test.client
import
RequestFactory
from
rest_framework
import
generics
,
status
,
serializers
from
rest_framework.tests.models
import
BasicModel
factory
=
RequestFactory
()
class
BasicList
(
generics
.
ListCreateAPIView
):
model
=
BasicModel
model_serializer_class
=
serializers
.
HyperlinkedModelSerializer
class
BasicDetail
(
generics
.
RetrieveUpdateDestroyAPIView
):
model
=
BasicModel
model_serializer_class
=
serializers
.
HyperlinkedModelSerializer
urlpatterns
=
patterns
(
''
,
url
(
r'^basic/$'
,
BasicList
.
as_view
(),
name
=
'basicmodel-list'
),
url
(
r'^basic/(?P<pk>\d+)/$'
,
BasicDetail
.
as_view
(),
name
=
'basicmodel-detail'
),
)
class
TestHyperlinkedView
(
TestCase
):
urls
=
'rest_framework.tests.hyperlinkedserializers'
def
setUp
(
self
):
"""
Create 3 BasicModel intances.
"""
items
=
[
'foo'
,
'bar'
,
'baz'
]
for
item
in
items
:
BasicModel
(
text
=
item
)
.
save
()
self
.
objects
=
BasicModel
.
objects
self
.
data
=
[
{
'url'
:
'http://testserver/basic/
%
d/'
%
obj
.
id
,
'text'
:
obj
.
text
}
for
obj
in
self
.
objects
.
all
()
]
self
.
list_view
=
BasicList
.
as_view
()
self
.
detail_view
=
BasicDetail
.
as_view
()
def
test_get_list_view
(
self
):
"""
GET requests to ListCreateAPIView should return list of objects.
"""
request
=
factory
.
get
(
'/'
)
response
=
self
.
list_view
(
request
)
.
render
()
self
.
assertEquals
(
response
.
status_code
,
status
.
HTTP_200_OK
)
self
.
assertEquals
(
response
.
data
,
self
.
data
)
def
test_get_detail_view
(
self
):
"""
GET requests to ListCreateAPIView should return list of objects.
"""
request
=
factory
.
get
(
'/1'
)
response
=
self
.
detail_view
(
request
,
pk
=
1
)
.
render
()
self
.
assertEquals
(
response
.
status_code
,
status
.
HTTP_200_OK
)
self
.
assertEquals
(
response
.
data
,
self
.
data
[
0
])
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