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
770ed3de
Commit
770ed3de
authored
May 18, 2013
by
Ryan Kaskel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ToMany fields default to read-only if targeting ManyToManyField.
parent
34776da9
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
89 additions
and
0 deletions
+89
-0
docs/api-guide/relations.md
+9
-0
rest_framework/serializers.py
+17
-0
rest_framework/tests/relations_pk.py
+63
-0
No files found.
docs/api-guide/relations.md
View file @
770ed3de
...
@@ -381,6 +381,15 @@ Note that reverse generic keys, expressed using the `GenericRelation` field, can
...
@@ -381,6 +381,15 @@ Note that reverse generic keys, expressed using the `GenericRelation` field, can
For more information see [the Django documentation on generic relations][generic-relations].
For more information see [the Django documentation on generic relations][generic-relations].
## ManyToManyFields with a Through Model
By default, relational fields that target a ``ManyToManyField`` with a
``
through`` model specified are set to read-only.
If you exlicitly specify a relational field pointing to a
``
ManyToManyField`` with a through model, be sure to set ``read_only``
to ``True``.
## Advanced Hyperlinked fields
## Advanced Hyperlinked fields
If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.
If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.
...
...
rest_framework/serializers.py
View file @
770ed3de
...
@@ -587,11 +587,16 @@ class ModelSerializer(Serializer):
...
@@ -587,11 +587,16 @@ class ModelSerializer(Serializer):
forward_rels
+=
[
field
for
field
in
opts
.
many_to_many
if
field
.
serialize
]
forward_rels
+=
[
field
for
field
in
opts
.
many_to_many
if
field
.
serialize
]
for
model_field
in
forward_rels
:
for
model_field
in
forward_rels
:
has_user_through_model
=
False
if
model_field
.
rel
:
if
model_field
.
rel
:
to_many
=
isinstance
(
model_field
,
to_many
=
isinstance
(
model_field
,
models
.
fields
.
related
.
ManyToManyField
)
models
.
fields
.
related
.
ManyToManyField
)
related_model
=
model_field
.
rel
.
to
related_model
=
model_field
.
rel
.
to
if
to_many
and
not
model_field
.
rel
.
through
.
_meta
.
auto_created
:
has_user_through_model
=
True
if
model_field
.
rel
and
nested
:
if
model_field
.
rel
and
nested
:
if
len
(
inspect
.
getargspec
(
self
.
get_nested_field
)
.
args
)
==
2
:
if
len
(
inspect
.
getargspec
(
self
.
get_nested_field
)
.
args
)
==
2
:
warnings
.
warn
(
warnings
.
warn
(
...
@@ -620,6 +625,9 @@ class ModelSerializer(Serializer):
...
@@ -620,6 +625,9 @@ class ModelSerializer(Serializer):
field
=
self
.
get_field
(
model_field
)
field
=
self
.
get_field
(
model_field
)
if
field
:
if
field
:
if
has_user_through_model
:
field
.
read_only
=
True
ret
[
model_field
.
name
]
=
field
ret
[
model_field
.
name
]
=
field
# Deal with reverse relationships
# Deal with reverse relationships
...
@@ -637,6 +645,12 @@ class ModelSerializer(Serializer):
...
@@ -637,6 +645,12 @@ class ModelSerializer(Serializer):
continue
continue
related_model
=
relation
.
model
related_model
=
relation
.
model
to_many
=
relation
.
field
.
rel
.
multiple
to_many
=
relation
.
field
.
rel
.
multiple
has_user_through_model
=
False
is_m2m
=
isinstance
(
relation
.
field
,
models
.
fields
.
related
.
ManyToManyField
)
if
is_m2m
and
not
relation
.
field
.
rel
.
through
.
_meta
.
auto_created
:
has_user_through_model
=
True
if
nested
:
if
nested
:
field
=
self
.
get_nested_field
(
None
,
related_model
,
to_many
)
field
=
self
.
get_nested_field
(
None
,
related_model
,
to_many
)
...
@@ -644,6 +658,9 @@ class ModelSerializer(Serializer):
...
@@ -644,6 +658,9 @@ class ModelSerializer(Serializer):
field
=
self
.
get_related_field
(
None
,
related_model
,
to_many
)
field
=
self
.
get_related_field
(
None
,
related_model
,
to_many
)
if
field
:
if
field
:
if
has_user_through_model
:
field
.
read_only
=
True
ret
[
accessor_name
]
=
field
ret
[
accessor_name
]
=
field
# Add the `read_only` flag to any fields that have bee specified
# Add the `read_only` flag to any fields that have bee specified
...
...
rest_framework/tests/relations_pk.py
View file @
770ed3de
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
from
django.db
import
models
from
django.test
import
TestCase
from
django.test
import
TestCase
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
rest_framework.tests.models
import
ManyToManyTarget
,
ManyToManySource
,
ForeignKeyTarget
,
ForeignKeySource
,
NullableForeignKeySource
,
OneToOneTarget
,
NullableOneToOneSource
from
rest_framework.tests.models
import
ManyToManyTarget
,
ManyToManySource
,
ForeignKeyTarget
,
ForeignKeySource
,
NullableForeignKeySource
,
OneToOneTarget
,
NullableOneToOneSource
...
@@ -124,6 +125,7 @@ class PKManyToManyTests(TestCase):
...
@@ -124,6 +125,7 @@ class PKManyToManyTests(TestCase):
# Ensure source 4 is added, and everything else is as expected
# Ensure source 4 is added, and everything else is as expected
queryset
=
ManyToManySource
.
objects
.
all
()
queryset
=
ManyToManySource
.
objects
.
all
()
serializer
=
ManyToManySourceSerializer
(
queryset
,
many
=
True
)
serializer
=
ManyToManySourceSerializer
(
queryset
,
many
=
True
)
self
.
assertFalse
(
serializer
.
fields
[
'targets'
]
.
read_only
)
expected
=
[
expected
=
[
{
'id'
:
1
,
'name'
:
'source-1'
,
'targets'
:
[
1
]},
{
'id'
:
1
,
'name'
:
'source-1'
,
'targets'
:
[
1
]},
{
'id'
:
2
,
'name'
:
'source-2'
,
'targets'
:
[
1
,
2
]},
{
'id'
:
2
,
'name'
:
'source-2'
,
'targets'
:
[
1
,
2
]},
...
@@ -135,6 +137,7 @@ class PKManyToManyTests(TestCase):
...
@@ -135,6 +137,7 @@ class PKManyToManyTests(TestCase):
def
test_reverse_many_to_many_create
(
self
):
def
test_reverse_many_to_many_create
(
self
):
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'sources'
:
[
1
,
3
]}
data
=
{
'id'
:
4
,
'name'
:
'target-4'
,
'sources'
:
[
1
,
3
]}
serializer
=
ManyToManyTargetSerializer
(
data
=
data
)
serializer
=
ManyToManyTargetSerializer
(
data
=
data
)
self
.
assertFalse
(
serializer
.
fields
[
'sources'
]
.
read_only
)
self
.
assertTrue
(
serializer
.
is_valid
())
self
.
assertTrue
(
serializer
.
is_valid
())
obj
=
serializer
.
save
()
obj
=
serializer
.
save
()
self
.
assertEqual
(
serializer
.
data
,
data
)
self
.
assertEqual
(
serializer
.
data
,
data
)
...
@@ -421,3 +424,63 @@ class PKNullableOneToOneTests(TestCase):
...
@@ -421,3 +424,63 @@ class PKNullableOneToOneTests(TestCase):
{
'id'
:
2
,
'name'
:
'target-2'
,
'nullable_source'
:
1
},
{
'id'
:
2
,
'name'
:
'target-2'
,
'nullable_source'
:
1
},
]
]
self
.
assertEqual
(
serializer
.
data
,
expected
)
self
.
assertEqual
(
serializer
.
data
,
expected
)
# The below models and tests ensure that serializer fields corresponding
# to a ManyToManyField field with a user-specified ``through`` model are
# set to read only
class
ManyToManyThroughTarget
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
100
)
class
ManyToManyThrough
(
models
.
Model
):
source
=
models
.
ForeignKey
(
'ManyToManyThroughSource'
)
target
=
models
.
ForeignKey
(
ManyToManyThroughTarget
)
class
ManyToManyThroughSource
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
100
)
targets
=
models
.
ManyToManyField
(
ManyToManyThroughTarget
,
related_name
=
'sources'
,
through
=
'ManyToManyThrough'
)
class
ManyToManyThroughTargetSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
ManyToManyThroughTarget
fields
=
(
'id'
,
'name'
,
'sources'
)
class
ManyToManyThroughSourceSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
ManyToManyThroughSource
fields
=
(
'id'
,
'name'
,
'targets'
)
class
PKManyToManyThroughTests
(
TestCase
):
def
setUp
(
self
):
self
.
source
=
ManyToManyThroughSource
.
objects
.
create
(
name
=
'through-source-1'
)
self
.
target
=
ManyToManyThroughTarget
.
objects
.
create
(
name
=
'through-target-1'
)
def
test_many_to_many_create
(
self
):
data
=
{
'id'
:
2
,
'name'
:
'source-2'
,
'targets'
:
[
self
.
target
.
pk
]}
serializer
=
ManyToManyThroughSourceSerializer
(
data
=
data
)
self
.
assertTrue
(
serializer
.
fields
[
'targets'
]
.
read_only
)
self
.
assertTrue
(
serializer
.
is_valid
())
obj
=
serializer
.
save
()
self
.
assertEqual
(
obj
.
name
,
'source-2'
)
self
.
assertEqual
(
obj
.
targets
.
count
(),
0
)
def
test_many_to_many_reverse_create
(
self
):
data
=
{
'id'
:
2
,
'name'
:
'target-2'
,
'sources'
:
[
self
.
source
.
pk
]}
serializer
=
ManyToManyThroughTargetSerializer
(
data
=
data
)
self
.
assertTrue
(
serializer
.
fields
[
'sources'
]
.
read_only
)
self
.
assertTrue
(
serializer
.
is_valid
())
serializer
.
save
()
obj
=
serializer
.
save
()
self
.
assertEqual
(
obj
.
name
,
'target-2'
)
self
.
assertEqual
(
obj
.
sources
.
count
(),
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