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
d97e72cd
Commit
d97e72cd
authored
Mar 25, 2013
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cleanup one-one nested tests and implementation
parent
3f79a9a3
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
157 additions
and
66 deletions
+157
-66
rest_framework/serializers.py
+31
-6
rest_framework/tests/relations_nested.py
+126
-60
No files found.
rest_framework/serializers.py
View file @
d97e72cd
...
...
@@ -667,9 +667,12 @@ class ModelSerializer(Serializer):
cls
=
self
.
opts
.
model
opts
=
get_concrete_model
(
cls
)
.
_meta
exclusions
=
[
field
.
name
for
field
in
opts
.
fields
+
opts
.
many_to_many
]
for
field_name
,
field
in
self
.
fields
.
items
():
field_name
=
field
.
source
or
field_name
if
field_name
in
exclusions
and
not
field
.
read_only
:
if
field_name
in
exclusions
\
and
not
field
.
read_only
\
and
not
isinstance
(
field
,
Serializer
):
exclusions
.
remove
(
field_name
)
return
exclusions
...
...
@@ -695,6 +698,7 @@ class ModelSerializer(Serializer):
"""
m2m_data
=
{}
related_data
=
{}
nested_forward_relations
=
{}
meta
=
self
.
opts
.
model
.
_meta
# Reverse fk or one-to-one relations
...
...
@@ -714,6 +718,12 @@ class ModelSerializer(Serializer):
if
field
.
name
in
attrs
:
m2m_data
[
field
.
name
]
=
attrs
.
pop
(
field
.
name
)
# Nested forward relations - These need to be marked so we can save
# them before saving the parent model instance.
for
field_name
in
attrs
.
keys
():
if
isinstance
(
self
.
fields
.
get
(
field_name
,
None
),
Serializer
):
nested_forward_relations
[
field_name
]
=
attrs
[
field_name
]
# Update an existing instance...
if
instance
is
not
None
:
for
key
,
val
in
attrs
.
items
():
...
...
@@ -729,6 +739,7 @@ class ModelSerializer(Serializer):
# at the point of save.
instance
.
_related_data
=
related_data
instance
.
_m2m_data
=
m2m_data
instance
.
_nested_forward_relations
=
nested_forward_relations
return
instance
...
...
@@ -744,6 +755,13 @@ class ModelSerializer(Serializer):
"""
Save the deserialized object and return it.
"""
if
getattr
(
obj
,
'_nested_forward_relations'
,
None
):
# Nested relationships need to be saved before we can save the
# parent instance.
for
field_name
,
sub_object
in
obj
.
_nested_forward_relations
.
items
():
self
.
save_object
(
sub_object
)
setattr
(
obj
,
field_name
,
sub_object
)
obj
.
save
(
**
kwargs
)
if
getattr
(
obj
,
'_m2m_data'
,
None
):
...
...
@@ -753,15 +771,22 @@ class ModelSerializer(Serializer):
if
getattr
(
obj
,
'_related_data'
,
None
):
for
accessor_name
,
related
in
obj
.
_related_data
.
items
():
if
related
is
None
:
previous
=
getattr
(
obj
,
accessor_name
,
related
)
if
previous
:
previous
.
delete
()
elif
isinstance
(
related
,
models
.
Model
):
field
=
self
.
fields
.
get
(
accessor_name
,
None
)
if
isinstance
(
field
,
Serializer
):
# TODO: Following will be needed for reverse FK
# if field.many:
# # Nested reverse fk relationship
# for related_item in related:
# fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name
# setattr(related_item, fk_field, obj)
# self.save_object(related_item)
# else:
# Nested reverse one-one relationship
fk_field
=
obj
.
_meta
.
get_field_by_name
(
accessor_name
)[
0
]
.
field
.
name
setattr
(
related
,
fk_field
,
obj
)
self
.
save_object
(
related
)
else
:
# Reverse FK or reverse one-one
setattr
(
obj
,
accessor_name
,
related
)
del
(
obj
.
_related_data
)
...
...
rest_framework/tests/relations_nested.py
View file @
d97e72cd
...
...
@@ -8,61 +8,46 @@ class OneToOneTarget(models.Model):
name
=
models
.
CharField
(
max_length
=
100
)
class
OneToOneTargetSource
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
100
)
target
=
models
.
OneToOneField
(
OneToOneTarget
,
null
=
True
,
blank
=
True
,
related_name
=
'target_source'
)
class
OneToOneSource
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
100
)
target_source
=
models
.
OneToOneField
(
OneToOneTargetSource
,
related_name
=
'source'
)
class
OneToOneSourceSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
OneToOneSource
exclude
=
(
'target_source'
,
)
target
=
models
.
OneToOneField
(
OneToOneTarget
,
related_name
=
'source'
)
class
OneToOneTargetSourceSerializer
(
serializers
.
ModelSerializer
):
source
=
OneToOneSourceSerializer
()
class
Meta
:
model
=
OneToOneTargetSource
exclude
=
(
'target'
,
)
class
ReverseNestedOneToOneTests
(
TestCase
):
def
setUp
(
self
):
class
OneToOneSourceSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
OneToOneSource
fields
=
(
'id'
,
'name'
)
class
OneToOneTargetSerializer
(
serializers
.
ModelSerializer
):
target_source
=
OneToOneTarget
SourceSerializer
()
class
OneToOneTargetSerializer
(
serializers
.
ModelSerializer
):
source
=
OneToOne
SourceSerializer
()
class
Meta
:
model
=
OneToOneTarget
class
Meta
:
model
=
OneToOneTarget
fields
=
(
'id'
,
'name'
,
'source'
)
self
.
Serializer
=
OneToOneTargetSerializer
class
NestedOneToOneTests
(
TestCase
):
def
setUp
(
self
):
for
idx
in
range
(
1
,
4
):
target
=
OneToOneTarget
(
name
=
'target-
%
d'
%
idx
)
target
.
save
()
target_source
=
OneToOneTargetSource
(
name
=
'target-source-
%
d'
%
idx
,
target
=
target
)
target_source
.
save
()
source
=
OneToOneSource
(
name
=
'source-
%
d'
%
idx
,
target_source
=
target_source
)
source
=
OneToOneSource
(
name
=
'source-
%
d'
%
idx
,
target
=
target
)
source
.
save
()
def
test_one_to_one_retrieve
(
self
):
queryset
=
OneToOneTarget
.
objects
.
all
()
serializer
=
OneToOneTarget
Serializer
(
queryset
)
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'target-1'
,
'
target_source'
:
{
'id'
:
1
,
'name'
:
'target-source-1'
,
'source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
target_source'
:
{
'id'
:
2
,
'name'
:
'target-source-2'
,
'source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}
}},
{
'id'
:
3
,
'name'
:
'target-3'
,
'
target_source'
:
{
'id'
:
3
,
'name'
:
'target-source-3'
,
'source'
:
{
'id'
:
3
,
'name'
:
'source-3'
}
}}
{
'id'
:
1
,
'name'
:
'target-1'
,
'
source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}},
{
'id'
:
3
,
'name'
:
'target-3'
,
'
source'
:
{
'id'
:
3
,
'name'
:
'source-3'
}}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
def
test_one_to_one_create
(
self
):
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'
target_source'
:
{
'id'
:
4
,
'name'
:
'target-source-4'
,
'source'
:
{
'id'
:
4
,
'name'
:
'source-4'
}
}}
serializer
=
OneToOneTarget
Serializer
(
data
=
data
)
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'
source'
:
{
'id'
:
4
,
'name'
:
'source-4'
}}
serializer
=
self
.
Serializer
(
data
=
data
)
self
.
assertTrue
(
serializer
.
is_valid
())
obj
=
serializer
.
save
()
self
.
assertEqual
(
serializer
.
data
,
data
)
...
...
@@ -71,25 +56,25 @@ class NestedOneToOneTests(TestCase):
# Ensure (target 4, target_source 4, source 4) are added, and
# everything else is as expected.
queryset
=
OneToOneTarget
.
objects
.
all
()
serializer
=
OneToOneTarget
Serializer
(
queryset
)
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'target-1'
,
'
target_source'
:
{
'id'
:
1
,
'name'
:
'target-source-1'
,
'source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
target_source'
:
{
'id'
:
2
,
'name'
:
'target-source-2'
,
'source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}
}},
{
'id'
:
3
,
'name'
:
'target-3'
,
'
target_source'
:
{
'id'
:
3
,
'name'
:
'target-source-3'
,
'source'
:
{
'id'
:
3
,
'name'
:
'source-3'
}
}},
{
'id'
:
4
,
'name'
:
'target-4'
,
'
target_source'
:
{
'id'
:
4
,
'name'
:
'target-source-4'
,
'source'
:
{
'id'
:
4
,
'name'
:
'source-4'
}
}}
{
'id'
:
1
,
'name'
:
'target-1'
,
'
source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}},
{
'id'
:
3
,
'name'
:
'target-3'
,
'
source'
:
{
'id'
:
3
,
'name'
:
'source-3'
}},
{
'id'
:
4
,
'name'
:
'target-4'
,
'
source'
:
{
'id'
:
4
,
'name'
:
'source-4'
}}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
def
test_one_to_one_create_with_invalid_data
(
self
):
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'
target_source'
:
{
'id'
:
4
,
'name'
:
'target-source-4'
,
'source'
:
{
'id'
:
4
}
}}
serializer
=
OneToOneTarget
Serializer
(
data
=
data
)
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'
source'
:
{
'id'
:
4
}}
serializer
=
self
.
Serializer
(
data
=
data
)
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertEqual
(
serializer
.
errors
,
{
'
target_source'
:
[{
'source'
:
[{
'name'
:
[
'This field is required.'
]}
]}]})
self
.
assertEqual
(
serializer
.
errors
,
{
'
source'
:
[{
'name'
:
[
'This field is required.'
]}]})
def
test_one_to_one_update
(
self
):
data
=
{
'id'
:
3
,
'name'
:
'target-3-updated'
,
'
target_source'
:
{
'id'
:
3
,
'name'
:
'target-source-3-updated'
,
'source'
:
{
'id'
:
3
,
'name'
:
'source-3-updated'
}
}}
data
=
{
'id'
:
3
,
'name'
:
'target-3-updated'
,
'
source'
:
{
'id'
:
3
,
'name'
:
'source-3-updated'
}}
instance
=
OneToOneTarget
.
objects
.
get
(
pk
=
3
)
serializer
=
OneToOneTarget
Serializer
(
instance
,
data
=
data
)
serializer
=
self
.
Serializer
(
instance
,
data
=
data
)
self
.
assertTrue
(
serializer
.
is_valid
())
obj
=
serializer
.
save
()
self
.
assertEqual
(
serializer
.
data
,
data
)
...
...
@@ -98,28 +83,109 @@ class NestedOneToOneTests(TestCase):
# Ensure (target 3, target_source 3, source 3) are updated,
# and everything else is as expected.
queryset
=
OneToOneTarget
.
objects
.
all
()
serializer
=
OneToOneTarget
Serializer
(
queryset
)
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'target-1'
,
'
target_source'
:
{
'id'
:
1
,
'name'
:
'target-source-1'
,
'source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
target_source'
:
{
'id'
:
2
,
'name'
:
'target-source-2'
,
'source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}
}},
{
'id'
:
3
,
'name'
:
'target-3-updated'
,
'
target_source'
:
{
'id'
:
3
,
'name'
:
'target-source-3-updated'
,
'source'
:
{
'id'
:
3
,
'name'
:
'source-3-updated'
}
}}
{
'id'
:
1
,
'name'
:
'target-1'
,
'
source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}},
{
'id'
:
2
,
'name'
:
'target-2'
,
'
source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}},
{
'id'
:
3
,
'name'
:
'target-3-updated'
,
'
source'
:
{
'id'
:
3
,
'name'
:
'source-3-updated'
}}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
def
test_one_to_one_delete
(
self
):
data
=
{
'id'
:
3
,
'name'
:
'target-3'
,
'target_source'
:
None
}
instance
=
OneToOneTarget
.
objects
.
get
(
pk
=
3
)
serializer
=
OneToOneTargetSerializer
(
instance
,
data
=
data
)
class
ForwardNestedOneToOneTests
(
TestCase
):
def
setUp
(
self
):
class
OneToOneTargetSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
OneToOneTarget
fields
=
(
'id'
,
'name'
)
class
OneToOneSourceSerializer
(
serializers
.
ModelSerializer
):
target
=
OneToOneTargetSerializer
()
class
Meta
:
model
=
OneToOneSource
fields
=
(
'id'
,
'name'
,
'target'
)
self
.
Serializer
=
OneToOneSourceSerializer
for
idx
in
range
(
1
,
4
):
target
=
OneToOneTarget
(
name
=
'target-
%
d'
%
idx
)
target
.
save
()
source
=
OneToOneSource
(
name
=
'source-
%
d'
%
idx
,
target
=
target
)
source
.
save
()
def
test_one_to_one_retrieve
(
self
):
queryset
=
OneToOneSource
.
objects
.
all
()
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'source-1'
,
'target'
:
{
'id'
:
1
,
'name'
:
'target-1'
}},
{
'id'
:
2
,
'name'
:
'source-2'
,
'target'
:
{
'id'
:
2
,
'name'
:
'target-2'
}},
{
'id'
:
3
,
'name'
:
'source-3'
,
'target'
:
{
'id'
:
3
,
'name'
:
'target-3'
}}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
def
test_one_to_one_create
(
self
):
data
=
{
'id'
:
4
,
'name'
:
'source-4'
,
'target'
:
{
'id'
:
4
,
'name'
:
'target-4'
}}
serializer
=
self
.
Serializer
(
data
=
data
)
self
.
assertTrue
(
serializer
.
is_valid
())
serializer
.
save
()
obj
=
serializer
.
save
()
self
.
assertEqual
(
serializer
.
data
,
data
)
self
.
assertEqual
(
obj
.
name
,
'source-4'
)
# Ensure (target 4, target_source 4, source 4) are added, and
# everything else is as expected.
queryset
=
OneToOneSource
.
objects
.
all
()
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'source-1'
,
'target'
:
{
'id'
:
1
,
'name'
:
'target-1'
}},
{
'id'
:
2
,
'name'
:
'source-2'
,
'target'
:
{
'id'
:
2
,
'name'
:
'target-2'
}},
{
'id'
:
3
,
'name'
:
'source-3'
,
'target'
:
{
'id'
:
3
,
'name'
:
'target-3'
}},
{
'id'
:
4
,
'name'
:
'source-4'
,
'target'
:
{
'id'
:
4
,
'name'
:
'target-4'
}}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
# Ensure (target_source 3, source 3) are deleted,
def
test_one_to_one_create_with_invalid_data
(
self
):
data
=
{
'id'
:
4
,
'name'
:
'source-4'
,
'target'
:
{
'id'
:
4
}}
serializer
=
self
.
Serializer
(
data
=
data
)
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertEqual
(
serializer
.
errors
,
{
'target'
:
[{
'name'
:
[
'This field is required.'
]}]})
def
test_one_to_one_update
(
self
):
data
=
{
'id'
:
3
,
'name'
:
'source-3-updated'
,
'target'
:
{
'id'
:
3
,
'name'
:
'target-3-updated'
}}
instance
=
OneToOneSource
.
objects
.
get
(
pk
=
3
)
serializer
=
self
.
Serializer
(
instance
,
data
=
data
)
self
.
assertTrue
(
serializer
.
is_valid
())
obj
=
serializer
.
save
()
self
.
assertEqual
(
serializer
.
data
,
data
)
self
.
assertEqual
(
obj
.
name
,
'source-3-updated'
)
# Ensure (target 3, target_source 3, source 3) are updated,
# and everything else is as expected.
queryset
=
OneToOne
Target
.
objects
.
all
()
serializer
=
OneToOneTarget
Serializer
(
queryset
)
queryset
=
OneToOne
Source
.
objects
.
all
()
serializer
=
self
.
Serializer
(
queryset
)
expected
=
[
{
'id'
:
1
,
'name'
:
'
target-1'
,
'target_source'
:
{
'id'
:
1
,
'name'
:
'target-source-1'
,
'source'
:
{
'id'
:
1
,
'name'
:
'source-1'
}
}},
{
'id'
:
2
,
'name'
:
'
target-2'
,
'target_source'
:
{
'id'
:
2
,
'name'
:
'target-source-2'
,
'source'
:
{
'id'
:
2
,
'name'
:
'source-2'
}
}},
{
'id'
:
3
,
'name'
:
'
target-3'
,
'target_source'
:
None
}
{
'id'
:
1
,
'name'
:
'
source-1'
,
'target'
:
{
'id'
:
1
,
'name'
:
'target-1'
}},
{
'id'
:
2
,
'name'
:
'
source-2'
,
'target'
:
{
'id'
:
2
,
'name'
:
'target-2'
}},
{
'id'
:
3
,
'name'
:
'
source-3-updated'
,
'target'
:
{
'id'
:
3
,
'name'
:
'target-3-updated'
}
}
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
# TODO: Nullable 1-1 tests
# def test_one_to_one_delete(self):
# data = {'id': 3, 'name': 'target-3', 'target_source': None}
# instance = OneToOneTarget.objects.get(pk=3)
# serializer = self.Serializer(instance, data=data)
# self.assertTrue(serializer.is_valid())
# serializer.save()
# # Ensure (target_source 3, source 3) are deleted,
# # and everything else is as expected.
# queryset = OneToOneTarget.objects.all()
# serializer = self.Serializer(queryset)
# expected = [
# {'id': 1, 'name': 'target-1', 'source': {'id': 1, 'name': 'source-1'}},
# {'id': 2, 'name': 'target-2', 'source': {'id': 2, 'name': 'source-2'}},
# {'id': 3, 'name': 'target-3', 'source': None}
# ]
# self.assertEqual(serializer.data, expected)
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