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
53a80044
Commit
53a80044
authored
May 18, 2013
by
Pablo Recio
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into 725-blank-choice-dash
Conflicts: rest_framework/tests/fields.py
parents
ab8bd566
2a3056d0
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
290 additions
and
43 deletions
+290
-43
.travis.yml
+9
-8
rest_framework/fields.py
+6
-1
rest_framework/relations.py
+11
-3
rest_framework/renderers.py
+33
-9
rest_framework/runtests/settings.py
+2
-0
rest_framework/tests/fields.py
+15
-2
rest_framework/tests/generics.py
+33
-1
rest_framework/tests/relations.py
+54
-1
rest_framework/tests/relations_hyperlink.py
+71
-0
rest_framework/tests/relations_pk.py
+56
-1
rest_framework/tests/serializer.py
+0
-17
No files found.
.travis.yml
View file @
53a80044
...
...
@@ -7,9 +7,9 @@ python:
-
"
3.3"
env
:
-
DJANGO="django==1.5 --use-mirrors"
-
DJANGO="django==1.4.
3
--use-mirrors"
-
DJANGO="django==1.3.
5
--use-mirrors"
-
DJANGO="django==1.5
.1
--use-mirrors"
-
DJANGO="django==1.4.
5
--use-mirrors"
-
DJANGO="django==1.3.
7
--use-mirrors"
install
:
-
pip install $DJANGO
...
...
@@ -18,7 +18,7 @@ install:
-
"
if
[[
${TRAVIS_PYTHON_VERSION::1}
!=
'3'
]];
then
pip
install
django-oauth-plus==2.0
--use-mirrors;
fi"
-
"
if
[[
${TRAVIS_PYTHON_VERSION::1}
!=
'3'
]];
then
pip
install
django-oauth2-provider==0.2.3
--use-mirrors;
fi"
-
"
if
[[
${DJANGO::11}
==
'django==1.3'
]];
then
pip
install
django-filter==0.5.4
--use-mirrors;
fi"
-
"
if
[[
${DJANGO::11}
!=
'django==1.3'
]];
then
pip
install
django-filter==0.6
a1
--use-mirrors;
fi"
-
"
if
[[
${DJANGO::11}
!=
'django==1.3'
]];
then
pip
install
django-filter==0.6
--use-mirrors;
fi"
-
export PYTHONPATH=.
script
:
...
...
@@ -27,10 +27,11 @@ script:
matrix
:
exclude
:
-
python
:
"
3.2"
env
:
DJANGO="django==1.4.
3
--use-mirrors"
env
:
DJANGO="django==1.4.
5
--use-mirrors"
-
python
:
"
3.2"
env
:
DJANGO="django==1.3.
5
--use-mirrors"
env
:
DJANGO="django==1.3.
7
--use-mirrors"
-
python
:
"
3.3"
env
:
DJANGO="django==1.4.
3
--use-mirrors"
env
:
DJANGO="django==1.4.
5
--use-mirrors"
-
python
:
"
3.3"
env
:
DJANGO="django==1.3.5 --use-mirrors"
env
:
DJANGO="django==1.3.7 --use-mirrors"
rest_framework/fields.py
View file @
53a80044
...
...
@@ -20,6 +20,7 @@ from django import forms
from
django.forms
import
widgets
from
django.utils.encoding
import
is_protected_type
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.datastructures
import
SortedDict
from
rest_framework
import
ISO_8601
from
rest_framework.compat
import
timezone
,
parse_date
,
parse_datetime
,
parse_time
...
...
@@ -171,7 +172,11 @@ class Field(object):
elif
hasattr
(
value
,
'__iter__'
)
and
not
isinstance
(
value
,
(
dict
,
six
.
string_types
)):
return
[
self
.
to_native
(
item
)
for
item
in
value
]
elif
isinstance
(
value
,
dict
):
return
dict
(
map
(
self
.
to_native
,
(
k
,
v
))
for
k
,
v
in
value
.
items
())
# Make sure we preserve field ordering, if it exists
ret
=
SortedDict
()
for
key
,
val
in
value
.
items
():
ret
[
key
]
=
self
.
to_native
(
val
)
return
ret
return
smart_text
(
value
)
def
attributes
(
self
):
...
...
rest_framework/relations.py
View file @
53a80044
...
...
@@ -221,12 +221,20 @@ class PrimaryKeyRelatedField(RelatedField):
def
field_to_native
(
self
,
obj
,
field_name
):
if
self
.
many
:
# To-many relationship
try
:
queryset
=
None
if
not
self
.
source
:
# Prefer obj.serializable_value for performance reasons
queryset
=
obj
.
serializable_value
(
self
.
source
or
field_name
)
try
:
queryset
=
obj
.
serializable_value
(
field_name
)
except
AttributeError
:
pass
if
queryset
is
None
:
# RelatedManager (reverse relationship)
queryset
=
getattr
(
obj
,
self
.
source
or
field_name
)
source
=
self
.
source
or
field_name
queryset
=
obj
for
component
in
source
.
split
(
'.'
):
queryset
=
get_component
(
queryset
,
component
)
# Forward relationship
return
[
self
.
to_native
(
item
.
pk
)
for
item
in
queryset
.
all
()]
...
...
rest_framework/renderers.py
View file @
53a80044
...
...
@@ -336,7 +336,7 @@ class BrowsableAPIRenderer(BaseRenderer):
return
# Cannot use form overloading
try
:
view
.
check_permissions
(
clone_request
(
request
,
method
)
)
view
.
check_permissions
(
request
)
except
exceptions
.
APIException
:
return
False
# Doesn't have permissions
return
True
...
...
@@ -372,6 +372,30 @@ class BrowsableAPIRenderer(BaseRenderer):
return
fields
def
_get_form
(
self
,
view
,
method
,
request
):
# We need to impersonate a request with the correct method,
# so that eg. any dynamic get_serializer_class methods return the
# correct form for each method.
restore
=
view
.
request
request
=
clone_request
(
request
,
method
)
view
.
request
=
request
try
:
return
self
.
get_form
(
view
,
method
,
request
)
finally
:
view
.
request
=
restore
def
_get_raw_data_form
(
self
,
view
,
method
,
request
,
media_types
):
# We need to impersonate a request with the correct method,
# so that eg. any dynamic get_serializer_class methods return the
# correct form for each method.
restore
=
view
.
request
request
=
clone_request
(
request
,
method
)
view
.
request
=
request
try
:
return
self
.
get_raw_data_form
(
view
,
method
,
request
,
media_types
)
finally
:
view
.
request
=
restore
def
get_form
(
self
,
view
,
method
,
request
):
"""
Get a form, possibly bound to either the input or output data.
...
...
@@ -465,15 +489,15 @@ class BrowsableAPIRenderer(BaseRenderer):
renderer
=
self
.
get_default_renderer
(
view
)
content
=
self
.
get_content
(
renderer
,
data
,
accepted_media_type
,
renderer_context
)
put_form
=
self
.
get_form
(
view
,
'PUT'
,
request
)
post_form
=
self
.
get_form
(
view
,
'POST'
,
request
)
patch_form
=
self
.
get_form
(
view
,
'PATCH'
,
request
)
delete_form
=
self
.
get_form
(
view
,
'DELETE'
,
request
)
options_form
=
self
.
get_form
(
view
,
'OPTIONS'
,
request
)
put_form
=
self
.
_
get_form
(
view
,
'PUT'
,
request
)
post_form
=
self
.
_
get_form
(
view
,
'POST'
,
request
)
patch_form
=
self
.
_
get_form
(
view
,
'PATCH'
,
request
)
delete_form
=
self
.
_
get_form
(
view
,
'DELETE'
,
request
)
options_form
=
self
.
_
get_form
(
view
,
'OPTIONS'
,
request
)
raw_data_put_form
=
self
.
get_raw_data_form
(
view
,
'PUT'
,
request
,
media_types
)
raw_data_post_form
=
self
.
get_raw_data_form
(
view
,
'POST'
,
request
,
media_types
)
raw_data_patch_form
=
self
.
get_raw_data_form
(
view
,
'PATCH'
,
request
,
media_types
)
raw_data_put_form
=
self
.
_
get_raw_data_form
(
view
,
'PUT'
,
request
,
media_types
)
raw_data_post_form
=
self
.
_
get_raw_data_form
(
view
,
'POST'
,
request
,
media_types
)
raw_data_patch_form
=
self
.
_
get_raw_data_form
(
view
,
'PATCH'
,
request
,
media_types
)
raw_data_put_or_patch_form
=
raw_data_put_form
or
raw_data_patch_form
name
=
self
.
get_name
(
view
)
...
...
rest_framework/runtests/settings.py
View file @
53a80044
...
...
@@ -4,6 +4,8 @@ DEBUG = True
TEMPLATE_DEBUG
=
DEBUG
DEBUG_PROPAGATE_EXCEPTIONS
=
True
ALLOWED_HOSTS
=
[
'*'
]
ADMINS
=
(
# ('Your Name', 'your_email@domain.com'),
)
...
...
rest_framework/tests/fields.py
View file @
53a80044
...
...
@@ -2,13 +2,12 @@
General serializer field tests.
"""
from
__future__
import
unicode_literals
from
django.utils.datastructures
import
SortedDict
import
datetime
from
decimal
import
Decimal
from
django.db
import
models
from
django.test
import
TestCase
from
django.core
import
validators
from
rest_framework
import
serializers
from
rest_framework.serializers
import
Serializer
...
...
@@ -63,6 +62,20 @@ class BasicFieldTests(TestCase):
serializer
=
CharPrimaryKeyModelSerializer
()
self
.
assertEqual
(
serializer
.
fields
[
'id'
]
.
read_only
,
False
)
def
test_dict_field_ordering
(
self
):
"""
Field should preserve dictionary ordering, if it exists.
See: https://github.com/tomchristie/django-rest-framework/issues/832
"""
ret
=
SortedDict
()
ret
[
'c'
]
=
1
ret
[
'b'
]
=
1
ret
[
'a'
]
=
1
ret
[
'z'
]
=
1
field
=
serializers
.
Field
()
keys
=
list
(
field
.
to_native
(
ret
)
.
keys
())
self
.
assertEqual
(
keys
,
[
'c'
,
'b'
,
'a'
,
'z'
])
class
DateFieldTest
(
TestCase
):
"""
...
...
rest_framework/tests/generics.py
View file @
53a80044
...
...
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from
django.db
import
models
from
django.shortcuts
import
get_object_or_404
from
django.test
import
TestCase
from
rest_framework
import
generics
,
serializers
,
status
from
rest_framework
import
generics
,
renderers
,
serializers
,
status
from
rest_framework.tests.utils
import
RequestFactory
from
rest_framework.tests.models
import
BasicModel
,
Comment
,
SlugBasedModel
from
rest_framework.compat
import
six
...
...
@@ -476,3 +476,35 @@ class TestFilterBackendAppliedToViews(TestCase):
response
=
instance_view
(
request
,
pk
=
1
)
.
render
()
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
self
.
assertEqual
(
response
.
data
,
{
'id'
:
1
,
'text'
:
'foo'
})
class
TwoFieldModel
(
models
.
Model
):
field_a
=
models
.
CharField
(
max_length
=
100
)
field_b
=
models
.
CharField
(
max_length
=
100
)
class
DynamicSerializerView
(
generics
.
ListCreateAPIView
):
model
=
TwoFieldModel
renderer_classes
=
(
renderers
.
BrowsableAPIRenderer
,
renderers
.
JSONRenderer
)
def
get_serializer_class
(
self
):
if
self
.
request
.
method
==
'POST'
:
class
DynamicSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
TwoFieldModel
fields
=
(
'field_b'
,)
return
DynamicSerializer
return
super
(
DynamicSerializerView
,
self
)
.
get_serializer_class
()
class
TestFilterBackendAppliedToViews
(
TestCase
):
def
test_dynamic_serializer_form_in_browsable_api
(
self
):
"""
GET requests to ListCreateAPIView should return filtered list.
"""
view
=
DynamicSerializerView
.
as_view
()
request
=
factory
.
get
(
'/'
)
response
=
view
(
request
)
.
render
()
self
.
assertContains
(
response
,
'field_b'
)
self
.
assertNotContains
(
response
,
'field_a'
)
rest_framework/tests/relations.py
View file @
53a80044
...
...
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
from
django.db
import
models
from
django.test
import
TestCase
from
rest_framework
import
serializers
from
rest_framework.tests.models
import
BlogPost
class
NullModel
(
models
.
Model
):
...
...
@@ -33,7 +34,7 @@ class FieldTests(TestCase):
self
.
assertRaises
(
serializers
.
ValidationError
,
field
.
from_native
,
[])
class
TestManyRelateMixin
(
TestCase
):
class
TestManyRelate
d
Mixin
(
TestCase
):
def
test_missing_many_to_many_related_field
(
self
):
'''
Regression test for #632
...
...
@@ -45,3 +46,55 @@ class TestManyRelateMixin(TestCase):
into
=
{}
field
.
field_from_native
({},
None
,
'field_name'
,
into
)
self
.
assertEqual
(
into
[
'field_name'
],
[])
# Regression tests for #694 (`source` attribute on related fields)
class
RelatedFieldSourceTests
(
TestCase
):
def
test_related_manager_source
(
self
):
"""
Relational fields should be able to use manager-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
RelatedField
(
many
=
True
,
source
=
'get_blogposts_manager'
)
class
ClassWithManagerMethod
(
object
):
def
get_blogposts_manager
(
self
):
return
BlogPost
.
objects
obj
=
ClassWithManagerMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'BlogPost object'
])
def
test_related_queryset_source
(
self
):
"""
Relational fields should be able to use queryset-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
RelatedField
(
many
=
True
,
source
=
'get_blogposts_queryset'
)
class
ClassWithQuerysetMethod
(
object
):
def
get_blogposts_queryset
(
self
):
return
BlogPost
.
objects
.
all
()
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'BlogPost object'
])
def
test_dotted_source
(
self
):
"""
Source argument should support dotted.source notation.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
RelatedField
(
many
=
True
,
source
=
'a.b.c'
)
class
ClassWithQuerysetMethod
(
object
):
a
=
{
'b'
:
{
'c'
:
BlogPost
.
objects
.
all
()
}
}
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'BlogPost object'
])
rest_framework/tests/relations_hyperlink.py
View file @
53a80044
...
...
@@ -4,6 +4,7 @@ from django.test.client import RequestFactory
from
rest_framework
import
serializers
from
rest_framework.compat
import
patterns
,
url
from
rest_framework.tests.models
import
(
BlogPost
,
ManyToManyTarget
,
ManyToManySource
,
ForeignKeyTarget
,
ForeignKeySource
,
NullableForeignKeySource
,
OneToOneTarget
,
NullableOneToOneSource
)
...
...
@@ -16,6 +17,7 @@ def dummy_view(request, pk):
pass
urlpatterns
=
patterns
(
''
,
url
(
r'^dummyurl/(?P<pk>[0-9]+)/$'
,
dummy_view
,
name
=
'dummy-url'
),
url
(
r'^manytomanysource/(?P<pk>[0-9]+)/$'
,
dummy_view
,
name
=
'manytomanysource-detail'
),
url
(
r'^manytomanytarget/(?P<pk>[0-9]+)/$'
,
dummy_view
,
name
=
'manytomanytarget-detail'
),
url
(
r'^foreignkeysource/(?P<pk>[0-9]+)/$'
,
dummy_view
,
name
=
'foreignkeysource-detail'
),
...
...
@@ -451,3 +453,72 @@ class HyperlinkedNullableOneToOneTests(TestCase):
{
'url'
:
'http://testserver/onetoonetarget/2/'
,
'name'
:
'target-2'
,
'nullable_source'
:
None
},
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
# Regression tests for #694 (`source` attribute on related fields)
class
HyperlinkedRelatedFieldSourceTests
(
TestCase
):
urls
=
'rest_framework.tests.relations_hyperlink'
def
test_related_manager_source
(
self
):
"""
Relational fields should be able to use manager-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
HyperlinkedRelatedField
(
many
=
True
,
source
=
'get_blogposts_manager'
,
view_name
=
'dummy-url'
,
)
field
.
context
=
{
'request'
:
request
}
class
ClassWithManagerMethod
(
object
):
def
get_blogposts_manager
(
self
):
return
BlogPost
.
objects
obj
=
ClassWithManagerMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'http://testserver/dummyurl/1/'
])
def
test_related_queryset_source
(
self
):
"""
Relational fields should be able to use queryset-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
HyperlinkedRelatedField
(
many
=
True
,
source
=
'get_blogposts_queryset'
,
view_name
=
'dummy-url'
,
)
field
.
context
=
{
'request'
:
request
}
class
ClassWithQuerysetMethod
(
object
):
def
get_blogposts_queryset
(
self
):
return
BlogPost
.
objects
.
all
()
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'http://testserver/dummyurl/1/'
])
def
test_dotted_source
(
self
):
"""
Source argument should support dotted.source notation.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
HyperlinkedRelatedField
(
many
=
True
,
source
=
'a.b.c'
,
view_name
=
'dummy-url'
,
)
field
.
context
=
{
'request'
:
request
}
class
ClassWithQuerysetMethod
(
object
):
a
=
{
'b'
:
{
'c'
:
BlogPost
.
objects
.
all
()
}
}
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
'http://testserver/dummyurl/1/'
])
rest_framework/tests/relations_pk.py
View file @
53a80044
from
__future__
import
unicode_literals
from
django.test
import
TestCase
from
rest_framework
import
serializers
from
rest_framework.tests.models
import
ManyToManyTarget
,
ManyToManySource
,
ForeignKeyTarget
,
ForeignKeySource
,
NullableForeignKeySource
,
OneToOneTarget
,
NullableOneToOneSource
from
rest_framework.tests.models
import
(
BlogPost
,
ManyToManyTarget
,
ManyToManySource
,
ForeignKeyTarget
,
ForeignKeySource
,
NullableForeignKeySource
,
OneToOneTarget
,
NullableOneToOneSource
,
)
from
rest_framework.compat
import
six
...
...
@@ -421,3 +424,55 @@ class PKNullableOneToOneTests(TestCase):
{
'id'
:
2
,
'name'
:
'target-2'
,
'nullable_source'
:
1
},
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
# Regression tests for #694 (`source` attribute on related fields)
class
PrimaryKeyRelatedFieldSourceTests
(
TestCase
):
def
test_related_manager_source
(
self
):
"""
Relational fields should be able to use manager-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
PrimaryKeyRelatedField
(
many
=
True
,
source
=
'get_blogposts_manager'
)
class
ClassWithManagerMethod
(
object
):
def
get_blogposts_manager
(
self
):
return
BlogPost
.
objects
obj
=
ClassWithManagerMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
1
])
def
test_related_queryset_source
(
self
):
"""
Relational fields should be able to use queryset-returning methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
PrimaryKeyRelatedField
(
many
=
True
,
source
=
'get_blogposts_queryset'
)
class
ClassWithQuerysetMethod
(
object
):
def
get_blogposts_queryset
(
self
):
return
BlogPost
.
objects
.
all
()
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
1
])
def
test_dotted_source
(
self
):
"""
Source argument should support dotted.source notation.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
field
=
serializers
.
PrimaryKeyRelatedField
(
many
=
True
,
source
=
'a.b.c'
)
class
ClassWithQuerysetMethod
(
object
):
a
=
{
'b'
:
{
'c'
:
BlogPost
.
objects
.
all
()
}
}
obj
=
ClassWithQuerysetMethod
()
value
=
field
.
field_to_native
(
obj
,
'field_name'
)
self
.
assertEqual
(
value
,
[
1
])
rest_framework/tests/serializer.py
View file @
53a80044
...
...
@@ -872,23 +872,6 @@ class RelatedTraversalTest(TestCase):
self
.
assertEqual
(
serializer
.
data
,
expected
)
def
test_queryset_nested_traversal
(
self
):
"""
Relational fields should be able to use methods as their source.
"""
BlogPost
.
objects
.
create
(
title
=
'blah'
)
class
QuerysetMethodSerializer
(
serializers
.
Serializer
):
blogposts
=
serializers
.
RelatedField
(
many
=
True
,
source
=
'get_all_blogposts'
)
class
ClassWithQuerysetMethod
(
object
):
def
get_all_blogposts
(
self
):
return
BlogPost
.
objects
obj
=
ClassWithQuerysetMethod
()
serializer
=
QuerysetMethodSerializer
(
obj
)
self
.
assertEqual
(
serializer
.
data
,
{
'blogposts'
:
[
'BlogPost object'
]})
class
SerializerMethodFieldTests
(
TestCase
):
def
setUp
(
self
):
...
...
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