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
5d247a65
Commit
5d247a65
authored
Oct 09, 2014
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
First pass on nested serializers in HTML
parent
babdc78e
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
194 additions
and
27 deletions
+194
-27
docs/tutorial/quickstart.md
+5
-3
rest_framework/compat.py
+15
-1
rest_framework/fields.py
+23
-5
rest_framework/relations.py
+17
-3
rest_framework/renderers.py
+9
-1
rest_framework/serializers.py
+27
-8
rest_framework/templates/rest_framework/fields/horizontal/fieldset.html
+3
-2
rest_framework/templates/rest_framework/fields/horizontal/list_fieldset.html
+13
-0
rest_framework/templates/rest_framework/fields/inline/fieldset.html
+3
-2
rest_framework/templates/rest_framework/fields/vertical/fieldset.html
+3
-2
rest_framework/templates/rest_framework/fields/vertical/list_fieldset.html
+7
-0
tests/test_bound_fields.py
+69
-0
No files found.
docs/tutorial/quickstart.md
View file @
5d247a65
...
@@ -26,11 +26,13 @@ Create a new Django project named `tutorial`, then start a new app called `quick
...
@@ -26,11 +26,13 @@ Create a new Django project named `tutorial`, then start a new app called `quick
Now sync your database for the first time:
Now sync your database for the first time:
python manage.py
syncdb
python manage.py
migrate
Make sure t
o create an initial user named
`admin`
with a password of
`password`
. We'll authenticate as that user later in our example.
We'll als
o create an initial user named
`admin`
with a password of
`password`
. We'll authenticate as that user later in our example.
Once you've set up a database and got everything synced and ready to go, open up the app's directory and we'll get coding...
python manage.py createsuperuser
Once you've set up a database and initial user created and ready to go, open up the app's directory and we'll get coding...
## Serializers
## Serializers
...
...
rest_framework/compat.py
View file @
5d247a65
...
@@ -114,12 +114,15 @@ else:
...
@@ -114,12 +114,15 @@ else:
# MinValueValidator
and MaxValueValidator
only accept `message` in 1.8+
# MinValueValidator
, MaxValueValidator et al.
only accept `message` in 1.8+
if
django
.
VERSION
>=
(
1
,
8
):
if
django
.
VERSION
>=
(
1
,
8
):
from
django.core.validators
import
MinValueValidator
,
MaxValueValidator
from
django.core.validators
import
MinValueValidator
,
MaxValueValidator
from
django.core.validators
import
MinLengthValidator
,
MaxLengthValidator
else
:
else
:
from
django.core.validators
import
MinValueValidator
as
DjangoMinValueValidator
from
django.core.validators
import
MinValueValidator
as
DjangoMinValueValidator
from
django.core.validators
import
MaxValueValidator
as
DjangoMaxValueValidator
from
django.core.validators
import
MaxValueValidator
as
DjangoMaxValueValidator
from
django.core.validators
import
MinLengthValidator
as
DjangoMinLengthValidator
from
django.core.validators
import
MaxLengthValidator
as
DjangoMaxLengthValidator
class
MinValueValidator
(
DjangoMinValueValidator
):
class
MinValueValidator
(
DjangoMinValueValidator
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
...
@@ -131,6 +134,17 @@ else:
...
@@ -131,6 +134,17 @@ else:
self
.
message
=
kwargs
.
pop
(
'message'
,
self
.
message
)
self
.
message
=
kwargs
.
pop
(
'message'
,
self
.
message
)
super
(
MaxValueValidator
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
MaxValueValidator
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
class
MinLengthValidator
(
DjangoMinLengthValidator
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
message
=
kwargs
.
pop
(
'message'
,
self
.
message
)
super
(
MinLengthValidator
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
class
MaxLengthValidator
(
DjangoMaxLengthValidator
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
message
=
kwargs
.
pop
(
'message'
,
self
.
message
)
super
(
MaxLengthValidator
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
# URLValidator only accepts `message` in 1.6+
# URLValidator only accepts `message` in 1.6+
if
django
.
VERSION
>=
(
1
,
6
):
if
django
.
VERSION
>=
(
1
,
6
):
from
django.core.validators
import
URLValidator
from
django.core.validators
import
URLValidator
...
...
rest_framework/fields.py
View file @
5d247a65
...
@@ -8,7 +8,10 @@ from django.utils.dateparse import parse_date, parse_datetime, parse_time
...
@@ -8,7 +8,10 @@ from django.utils.dateparse import parse_date, parse_datetime, parse_time
from
django.utils.encoding
import
is_protected_type
from
django.utils.encoding
import
is_protected_type
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
rest_framework
import
ISO_8601
from
rest_framework
import
ISO_8601
from
rest_framework.compat
import
smart_text
,
EmailValidator
,
MinValueValidator
,
MaxValueValidator
,
URLValidator
from
rest_framework.compat
import
(
smart_text
,
EmailValidator
,
MinValueValidator
,
MaxValueValidator
,
MinLengthValidator
,
MaxLengthValidator
,
URLValidator
)
from
rest_framework.settings
import
api_settings
from
rest_framework.settings
import
api_settings
from
rest_framework.utils
import
html
,
representation
,
humanize_datetime
from
rest_framework.utils
import
html
,
representation
,
humanize_datetime
import
copy
import
copy
...
@@ -138,7 +141,7 @@ class Field(object):
...
@@ -138,7 +141,7 @@ class Field(object):
self
.
label
=
label
self
.
label
=
label
self
.
help_text
=
help_text
self
.
help_text
=
help_text
self
.
style
=
{}
if
style
is
None
else
style
self
.
style
=
{}
if
style
is
None
else
style
self
.
validators
=
validators
or
self
.
default_validators
[:]
self
.
validators
=
validators
[:]
or
self
.
default_validators
[:]
self
.
allow_null
=
allow_null
self
.
allow_null
=
allow_null
# These are set up by `.bind()` when the field is added to a serializer.
# These are set up by `.bind()` when the field is added to a serializer.
...
@@ -412,16 +415,24 @@ class NullBooleanField(Field):
...
@@ -412,16 +415,24 @@ class NullBooleanField(Field):
class
CharField
(
Field
):
class
CharField
(
Field
):
default_error_messages
=
{
default_error_messages
=
{
'blank'
:
_
(
'This field may not be blank.'
)
'blank'
:
_
(
'This field may not be blank.'
),
'max_length'
:
_
(
'Ensure this field has no more than {max_length} characters.'
),
'min_length'
:
_
(
'Ensure this field has no more than {min_length} characters.'
)
}
}
initial
=
''
initial
=
''
coerce_blank_to_null
=
False
coerce_blank_to_null
=
False
def
__init__
(
self
,
**
kwargs
):
def
__init__
(
self
,
**
kwargs
):
self
.
allow_blank
=
kwargs
.
pop
(
'allow_blank'
,
False
)
self
.
allow_blank
=
kwargs
.
pop
(
'allow_blank'
,
False
)
self
.
max_length
=
kwargs
.
pop
(
'max_length'
,
None
)
max_length
=
kwargs
.
pop
(
'max_length'
,
None
)
self
.
min_length
=
kwargs
.
pop
(
'min_length'
,
None
)
min_length
=
kwargs
.
pop
(
'min_length'
,
None
)
super
(
CharField
,
self
)
.
__init__
(
**
kwargs
)
super
(
CharField
,
self
)
.
__init__
(
**
kwargs
)
if
max_length
is
not
None
:
message
=
self
.
error_messages
[
'max_length'
]
.
format
(
max_length
=
max_length
)
self
.
validators
.
append
(
MaxLengthValidator
(
max_length
,
message
=
message
))
if
min_length
is
not
None
:
message
=
self
.
error_messages
[
'min_length'
]
.
format
(
min_length
=
min_length
)
self
.
validators
.
append
(
MinLengthValidator
(
min_length
,
message
=
message
))
def
run_validation
(
self
,
data
=
empty
):
def
run_validation
(
self
,
data
=
empty
):
# Test for the empty string here so that it does not get validated,
# Test for the empty string here so that it does not get validated,
...
@@ -857,6 +868,13 @@ class MultipleChoiceField(ChoiceField):
...
@@ -857,6 +868,13 @@ class MultipleChoiceField(ChoiceField):
}
}
default_empty_html
=
[]
default_empty_html
=
[]
def
get_value
(
self
,
dictionary
):
# We override the default field access in order to support
# lists in HTML forms.
if
html
.
is_html_input
(
dictionary
):
return
dictionary
.
getlist
(
self
.
field_name
)
return
dictionary
.
get
(
self
.
field_name
,
empty
)
def
to_internal_value
(
self
,
data
):
def
to_internal_value
(
self
,
data
):
if
isinstance
(
data
,
type
(
''
))
or
not
hasattr
(
data
,
'__iter__'
):
if
isinstance
(
data
,
type
(
''
))
or
not
hasattr
(
data
,
'__iter__'
):
self
.
fail
(
'not_a_list'
,
input_type
=
type
(
data
)
.
__name__
)
self
.
fail
(
'not_a_list'
,
input_type
=
type
(
data
)
.
__name__
)
...
...
rest_framework/relations.py
View file @
5d247a65
from
rest_framework.compat
import
smart_text
,
urlparse
from
rest_framework.compat
import
smart_text
,
urlparse
from
rest_framework.fields
import
empty
,
Field
from
rest_framework.fields
import
empty
,
Field
from
rest_framework.reverse
import
reverse
from
rest_framework.reverse
import
reverse
from
rest_framework.utils
import
html
from
django.core.exceptions
import
ObjectDoesNotExist
,
ImproperlyConfigured
from
django.core.exceptions
import
ObjectDoesNotExist
,
ImproperlyConfigured
from
django.core.urlresolvers
import
resolve
,
get_script_prefix
,
NoReverseMatch
,
Resolver404
from
django.core.urlresolvers
import
resolve
,
get_script_prefix
,
NoReverseMatch
,
Resolver404
from
django.db.models.query
import
QuerySet
from
django.db.models.query
import
QuerySet
...
@@ -263,6 +264,13 @@ class ManyRelation(Field):
...
@@ -263,6 +264,13 @@ class ManyRelation(Field):
super
(
ManyRelation
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
ManyRelation
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
child_relation
.
bind
(
field_name
=
''
,
parent
=
self
)
self
.
child_relation
.
bind
(
field_name
=
''
,
parent
=
self
)
def
get_value
(
self
,
dictionary
):
# We override the default field access in order to support
# lists in HTML forms.
if
html
.
is_html_input
(
dictionary
):
return
dictionary
.
getlist
(
self
.
field_name
)
return
dictionary
.
get
(
self
.
field_name
,
empty
)
def
to_internal_value
(
self
,
data
):
def
to_internal_value
(
self
,
data
):
return
[
return
[
self
.
child_relation
.
to_internal_value
(
item
)
self
.
child_relation
.
to_internal_value
(
item
)
...
@@ -278,10 +286,16 @@ class ManyRelation(Field):
...
@@ -278,10 +286,16 @@ class ManyRelation(Field):
@property
@property
def
choices
(
self
):
def
choices
(
self
):
queryset
=
self
.
child_relation
.
queryset
iterable
=
queryset
.
all
()
if
(
hasattr
(
queryset
,
'all'
))
else
queryset
items_and_representations
=
[
(
item
,
self
.
child_relation
.
to_representation
(
item
))
for
item
in
iterable
]
return
dict
([
return
dict
([
(
(
str
(
self
.
child_relation
.
to_representation
(
item
)
),
str
(
item_representation
),
str
(
item
)
str
(
item
)
+
' - '
+
str
(
item_representation
)
)
)
for
item
in
self
.
child_relation
.
queryset
.
all
()
for
item
,
item_representation
in
items_and_representations
])
])
rest_framework/renderers.py
View file @
5d247a65
...
@@ -364,6 +364,12 @@ class HTMLFormRenderer(BaseRenderer):
...
@@ -364,6 +364,12 @@ class HTMLFormRenderer(BaseRenderer):
serializers
.
ManyRelation
:
{
serializers
.
ManyRelation
:
{
'default'
:
'select_multiple.html'
,
'default'
:
'select_multiple.html'
,
'checkbox'
:
'select_checkbox.html'
'checkbox'
:
'select_checkbox.html'
},
serializers
.
Serializer
:
{
'default'
:
'fieldset.html'
},
serializers
.
ListSerializer
:
{
'default'
:
'list_fieldset.html'
}
}
})
})
...
@@ -392,7 +398,9 @@ class HTMLFormRenderer(BaseRenderer):
...
@@ -392,7 +398,9 @@ class HTMLFormRenderer(BaseRenderer):
template
=
loader
.
get_template
(
template_name
)
template
=
loader
.
get_template
(
template_name
)
context
=
Context
({
context
=
Context
({
'field'
:
field
,
'field'
:
field
,
'input_type'
:
input_type
'input_type'
:
input_type
,
'renderer'
:
self
,
'layout'
:
layout
})
})
return
template
.
render
(
context
)
return
template
.
render
(
context
)
...
...
rest_framework/serializers.py
View file @
5d247a65
...
@@ -166,14 +166,25 @@ class BoundField(object):
...
@@ -166,14 +166,25 @@ class BoundField(object):
Returned when iterating over a serializer instance,
Returned when iterating over a serializer instance,
providing an API similar to Django forms and form fields.
providing an API similar to Django forms and form fields.
"""
"""
def
__init__
(
self
,
field
,
value
,
errors
):
def
__init__
(
self
,
field
,
value
,
errors
,
prefix
=
''
):
self
.
_field
=
field
self
.
_field
=
field
self
.
value
=
value
self
.
value
=
value
self
.
errors
=
errors
self
.
errors
=
errors
self
.
name
=
prefix
+
self
.
field_name
def
__getattr__
(
self
,
attr_name
):
def
__getattr__
(
self
,
attr_name
):
return
getattr
(
self
.
_field
,
attr_name
)
return
getattr
(
self
.
_field
,
attr_name
)
def
__iter__
(
self
):
for
field
in
self
.
fields
.
values
():
yield
self
[
field
.
field_name
]
def
__getitem__
(
self
,
key
):
field
=
self
.
fields
[
key
]
value
=
self
.
value
.
get
(
key
)
if
self
.
value
else
None
error
=
self
.
errors
.
get
(
key
)
if
self
.
errors
else
None
return
BoundField
(
field
,
value
,
error
,
prefix
=
self
.
name
+
'.'
)
@property
@property
def
_proxy_class
(
self
):
def
_proxy_class
(
self
):
return
self
.
_field
.
__class__
return
self
.
_field
.
__class__
...
@@ -355,15 +366,22 @@ class Serializer(BaseSerializer):
...
@@ -355,15 +366,22 @@ class Serializer(BaseSerializer):
def
validate
(
self
,
attrs
):
def
validate
(
self
,
attrs
):
return
attrs
return
attrs
def
__repr__
(
self
):
return
representation
.
serializer_repr
(
self
,
indent
=
1
)
# The following are used for accessing `BoundField` instances on the
# serializer, for the purposes of presenting a form-like API onto the
# field values and field errors.
def
__iter__
(
self
):
def
__iter__
(
self
):
errors
=
self
.
errors
if
hasattr
(
self
,
'_errors'
)
else
{}
for
field
in
self
.
fields
.
values
():
for
field
in
self
.
fields
.
values
():
value
=
self
.
data
.
get
(
field
.
field_name
)
if
self
.
data
else
None
yield
self
[
field
.
field_name
]
error
=
errors
.
get
(
field
.
field_name
)
yield
BoundField
(
field
,
value
,
error
)
def
__repr__
(
self
):
def
__getitem__
(
self
,
key
):
return
representation
.
serializer_repr
(
self
,
indent
=
1
)
field
=
self
.
fields
[
key
]
value
=
self
.
data
.
get
(
key
)
error
=
self
.
errors
.
get
(
key
)
if
hasattr
(
self
,
'_errors'
)
else
None
return
BoundField
(
field
,
value
,
error
)
# There's some replication of `ListField` here,
# There's some replication of `ListField` here,
...
@@ -404,8 +422,9 @@ class ListSerializer(BaseSerializer):
...
@@ -404,8 +422,9 @@ class ListSerializer(BaseSerializer):
"""
"""
List of object instances -> List of dicts of primitive datatypes.
List of object instances -> List of dicts of primitive datatypes.
"""
"""
iterable
=
data
.
all
()
if
(
hasattr
(
data
,
'all'
))
else
data
return
ReturnList
(
return
ReturnList
(
[
self
.
child
.
to_representation
(
item
)
for
item
in
data
],
[
self
.
child
.
to_representation
(
item
)
for
item
in
iterable
],
serializer
=
self
serializer
=
self
)
)
...
...
rest_framework/templates/rest_framework/fields/horizontal/fieldset.html
View file @
5d247a65
{% load rest_framework %}
<fieldset>
<fieldset>
{% if field.label %}
{% if field.label %}
<div
class=
"form-group"
style=
"border-bottom: 1px solid #e5e5e5"
>
<div
class=
"form-group"
style=
"border-bottom: 1px solid #e5e5e5"
>
<legend
class=
"control-label col-sm-2 {% if field.style.hide_label %}sr-only{% endif %}"
style=
"border-bottom: 0"
>
{{ field.label }}
</legend>
<legend
class=
"control-label col-sm-2 {% if field.style.hide_label %}sr-only{% endif %}"
style=
"border-bottom: 0"
>
{{ field.label }}
</legend>
</div>
</div>
{% endif %}
{% endif %}
{% for
field_item in field.value.field_items.values()
%}
{% for
nested_field in field
%}
{
{ renderer.render_field(field_item, layout=layout) }
}
{
% render_field nested_field layout=layout renderer=renderer %
}
{% endfor %}
{% endfor %}
</fieldset>
</fieldset>
rest_framework/templates/rest_framework/fields/horizontal/list_fieldset.html
0 → 100644
View file @
5d247a65
{% load rest_framework %}
<fieldset>
{% if field.label %}
<div
class=
"form-group"
style=
"border-bottom: 1px solid #e5e5e5"
>
<legend
class=
"control-label col-sm-2 {% if field.style.hide_label %}sr-only{% endif %}"
style=
"border-bottom: 0"
>
{{ field.label }}
</legend>
</div>
{% endif %}
<ul>
{% for child in field.value %}
<li>
TODO
</li>
{% endfor %}
</ul>
</fieldset>
rest_framework/templates/rest_framework/fields/inline/fieldset.html
View file @
5d247a65
{% for field_item in field.value.field_items.values() %}
{% load rest_framework %}
{{ renderer.render_field(field_item, layout=layout) }}
{% for nested_field in field %}
{% render_field nested_field layout=layout renderer=renderer %}
{% endfor %}
{% endfor %}
rest_framework/templates/rest_framework/fields/vertical/fieldset.html
View file @
5d247a65
{% load rest_framework %}
<fieldset>
<fieldset>
{% if field.label %}
<legend
{%
if
field
.
style
.
hide_label
%}
class=
"sr-only"
{%
endif
%}
>
{{ field.label }}
</legend>
{% endif %}
{% if field.label %}
<legend
{%
if
field
.
style
.
hide_label
%}
class=
"sr-only"
{%
endif
%}
>
{{ field.label }}
</legend>
{% endif %}
{% for
field_item in field.value.field_items.values()
%}
{% for
nested_field in field
%}
{
{ renderer.render_field(field_item, layout=layout) }
}
{
% render_field nested_field layout=layout renderer=renderer %
}
{% endfor %}
{% endfor %}
</fieldset>
</fieldset>
rest_framework/templates/rest_framework/fields/vertical/list_fieldset.html
0 → 100644
View file @
5d247a65
<fieldset>
{% if field.label %}
<legend
{%
if
field
.
style
.
hide_label
%}
class=
"sr-only"
{%
endif
%}
>
{{ field.label }}
</legend>
{% endif %}
<!-- {% if field.label %}<legend {% if field.style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</legend>{% endif %}
{% for field_item in field.value.field_items.values() %}
{{ renderer.render_field(field_item, layout=layout) }}
{% endfor %} -->
</fieldset>
tests/test_bound_fields.py
0 → 100644
View file @
5d247a65
from
rest_framework
import
serializers
class
TestSimpleBoundField
:
def
test_empty_bound_field
(
self
):
class
ExampleSerializer
(
serializers
.
Serializer
):
text
=
serializers
.
CharField
(
max_length
=
100
)
amount
=
serializers
.
IntegerField
()
serializer
=
ExampleSerializer
()
assert
serializer
[
'text'
]
.
value
==
''
assert
serializer
[
'text'
]
.
errors
is
None
assert
serializer
[
'text'
]
.
name
==
'text'
assert
serializer
[
'amount'
]
.
value
is
None
assert
serializer
[
'amount'
]
.
errors
is
None
assert
serializer
[
'amount'
]
.
name
==
'amount'
def
test_populated_bound_field
(
self
):
class
ExampleSerializer
(
serializers
.
Serializer
):
text
=
serializers
.
CharField
(
max_length
=
100
)
amount
=
serializers
.
IntegerField
()
serializer
=
ExampleSerializer
(
data
=
{
'text'
:
'abc'
,
'amount'
:
123
})
assert
serializer
[
'text'
]
.
value
==
'abc'
assert
serializer
[
'text'
]
.
errors
is
None
assert
serializer
[
'text'
]
.
name
==
'text'
assert
serializer
[
'amount'
]
.
value
is
123
assert
serializer
[
'amount'
]
.
errors
is
None
assert
serializer
[
'amount'
]
.
name
==
'amount'
def
test_error_bound_field
(
self
):
class
ExampleSerializer
(
serializers
.
Serializer
):
text
=
serializers
.
CharField
(
max_length
=
100
)
amount
=
serializers
.
IntegerField
()
serializer
=
ExampleSerializer
(
data
=
{
'text'
:
'x'
*
1000
,
'amount'
:
123
})
serializer
.
is_valid
()
assert
serializer
[
'text'
]
.
value
==
'x'
*
1000
assert
serializer
[
'text'
]
.
errors
==
[
'Ensure this field has no more than 100 characters.'
]
assert
serializer
[
'text'
]
.
name
==
'text'
assert
serializer
[
'amount'
]
.
value
is
123
assert
serializer
[
'amount'
]
.
errors
is
None
assert
serializer
[
'amount'
]
.
name
==
'amount'
class
TestNestedBoundField
:
def
test_nested_empty_bound_field
(
self
):
class
Nested
(
serializers
.
Serializer
):
more_text
=
serializers
.
CharField
(
max_length
=
100
)
amount
=
serializers
.
IntegerField
()
class
ExampleSerializer
(
serializers
.
Serializer
):
text
=
serializers
.
CharField
(
max_length
=
100
)
nested
=
Nested
()
serializer
=
ExampleSerializer
()
assert
serializer
[
'text'
]
.
value
==
''
assert
serializer
[
'text'
]
.
errors
is
None
assert
serializer
[
'text'
]
.
name
==
'text'
assert
serializer
[
'nested'
][
'more_text'
]
.
value
==
''
assert
serializer
[
'nested'
][
'more_text'
]
.
errors
is
None
assert
serializer
[
'nested'
][
'more_text'
]
.
name
==
'nested.more_text'
assert
serializer
[
'nested'
][
'amount'
]
.
value
is
None
assert
serializer
[
'nested'
][
'amount'
]
.
errors
is
None
assert
serializer
[
'nested'
][
'amount'
]
.
name
==
'nested.amount'
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