mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-08 06:14:47 +03:00
tests for examples listed in spec
This commit is contained in:
parent
7fa40cfa57
commit
19fa92ebd3
|
@ -18,7 +18,7 @@ def parse_json_form(dictionary, prefix=''):
|
|||
"""
|
||||
# Step 1: Initialize output object
|
||||
output = {}
|
||||
for name, value in dictionary.items():
|
||||
for name, value in get_all_items(dictionary):
|
||||
# TODO: implement is_file flag
|
||||
|
||||
# Step 2: Compute steps array
|
||||
|
@ -40,6 +40,8 @@ def parse_json_form(dictionary, prefix=''):
|
|||
entry_value=value,
|
||||
is_file=False,
|
||||
)
|
||||
# Convert any remaining Undefined array entries to None
|
||||
output = clean_undefined(output)
|
||||
|
||||
# Account for DRF prefix (not part of JSON form spec)
|
||||
result = get_value(output, prefix, Undefined())
|
||||
|
@ -101,7 +103,7 @@ def parse_json_path(path):
|
|||
# Step 8 - Loop
|
||||
while path:
|
||||
# Step 8.1 - Check for single-item array
|
||||
if path[:1] == "[]":
|
||||
if path[:2] == "[]":
|
||||
steps[-1].append = True
|
||||
path = path[2:]
|
||||
if path:
|
||||
|
@ -153,8 +155,6 @@ def set_json_value(context, step, current_value, entry_value, is_file):
|
|||
# TODO: handle is_file
|
||||
|
||||
# Add empty values to array so indexing works like JavaScript
|
||||
# TODO: According to spec, these should turn into nulls if they are never
|
||||
# overriden in a later step.
|
||||
if isinstance(context, list) and isinstance(step.key, int):
|
||||
while len(context) <= step.key:
|
||||
context.append(Undefined())
|
||||
|
@ -163,13 +163,22 @@ def set_json_value(context, step, current_value, entry_value, is_file):
|
|||
if step.last:
|
||||
if isinstance(current_value, Undefined):
|
||||
# Step 7.1: No existing value
|
||||
key = step.key
|
||||
if isinstance(context, dict) and isinstance(key, int):
|
||||
key = str(key)
|
||||
if step.append:
|
||||
context[step.key] = [entry_value]
|
||||
context[key] = [entry_value]
|
||||
else:
|
||||
context[step.key] = entry_value
|
||||
context[key] = entry_value
|
||||
elif isinstance(current_value, list):
|
||||
# Step 7.2: Existing value is an Array
|
||||
# Step 7.2: Existing value is an Array, assume multi-valued field
|
||||
# and add entry to end.
|
||||
|
||||
# FIXME: What if the other items in the array had explicit keys and
|
||||
# this one is supposed to be the "" value?
|
||||
# (See step 8.4 and Example 7)
|
||||
context[step.key].append(entry_value)
|
||||
|
||||
elif isinstance(current_value, dict) and not is_file:
|
||||
# Step 7.3: Existing value is an Object
|
||||
return set_json_value(
|
||||
|
@ -195,20 +204,21 @@ def set_json_value(context, step, current_value, entry_value, is_file):
|
|||
context[step.key] = {}
|
||||
return context[step.key]
|
||||
elif isinstance(current_value, dict):
|
||||
# Step 7.2: Existing value is an Object
|
||||
# Step 8.2: Existing value is an Object
|
||||
return get_value(context, step.key, Undefined())
|
||||
elif isinstance(current_value, list):
|
||||
# Step 7.3: Existing value is an Array
|
||||
# Step 8.3: Existing value is an Array
|
||||
if step.next_type == "array":
|
||||
return current_value
|
||||
# Convert array to object to facilitate mixed keys
|
||||
obj = {}
|
||||
for i, item in enumerate(current_value):
|
||||
if not isinstance(item, Undefined):
|
||||
obj[i] = item
|
||||
obj[str(i)] = item
|
||||
context[step.key] = obj
|
||||
return obj
|
||||
else:
|
||||
# 7.4: Existing value is a scalar; convert to Object, preserving
|
||||
# 8.4: Existing value is a scalar; convert to Object, preserving
|
||||
# current value via an empty key
|
||||
obj = {'': current_value}
|
||||
context[step.key] = obj
|
||||
|
@ -255,3 +265,32 @@ class JsonStep(object):
|
|||
append = None
|
||||
last = None
|
||||
failed = None
|
||||
|
||||
|
||||
def clean_undefined(obj):
|
||||
"""
|
||||
Convert Undefined array entries to None (null)
|
||||
"""
|
||||
if isinstance(obj, list):
|
||||
return [
|
||||
None if isinstance(item, Undefined) else item
|
||||
for item in obj
|
||||
]
|
||||
if isinstance(obj, dict):
|
||||
for key in obj:
|
||||
obj[key] = clean_undefined(obj[key])
|
||||
return obj
|
||||
|
||||
|
||||
def get_all_items(obj):
|
||||
"""
|
||||
dict.items() but with a separate row for each value in a MultiValueDict
|
||||
"""
|
||||
if hasattr(obj, 'getlist'):
|
||||
items = []
|
||||
for key in obj:
|
||||
for value in obj.getlist(key):
|
||||
items.append((key, value))
|
||||
return items
|
||||
else:
|
||||
return obj.items()
|
||||
|
|
178
tests/test_json_forms.py
Normal file
178
tests/test_json_forms.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
from rest_framework.utils import html
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
||||
|
||||
class TestHtmlJsonExamples:
|
||||
"""
|
||||
Tests for the HTML JSON form algorithm
|
||||
Examples 1-10 from http://www.w3.org/TR/html-json-forms/
|
||||
"""
|
||||
|
||||
def test_basic_keys(self):
|
||||
"""
|
||||
Example 1: Basic Keys
|
||||
"""
|
||||
input_data = {
|
||||
'name': "Bender",
|
||||
'hind': "Bitable",
|
||||
'shiny': True
|
||||
}
|
||||
expected_output = input_data
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_multiple_values(self):
|
||||
"""
|
||||
Example 2: Multiple Values
|
||||
"""
|
||||
input_data = MultiValueDict()
|
||||
input_data.setlist('bottle-on-wall', [1, 2, 3])
|
||||
expected_output = {
|
||||
'bottle-on-wall': [1, 2, 3]
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_deeper_structure(self):
|
||||
"""
|
||||
Example 3: Deeper Structure
|
||||
"""
|
||||
input_data = {
|
||||
'pet[species]': "Dahut",
|
||||
'pet[name]': "Hypatia",
|
||||
'kids[1]': "Thelma",
|
||||
'kids[0]': "Ashley",
|
||||
}
|
||||
expected_output = {
|
||||
'pet': {
|
||||
'species': "Dahut",
|
||||
'name': "Hypatia",
|
||||
},
|
||||
'kids': ["Ashley", "Thelma"],
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_sparse_arrays(self):
|
||||
"""
|
||||
Example 4: Sparse Arrays
|
||||
"""
|
||||
input_data = {
|
||||
"hearbeat[0]": "thunk",
|
||||
"hearbeat[2]": "thunk",
|
||||
}
|
||||
expected_output = {
|
||||
"hearbeat": ["thunk", None, "thunk"]
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_even_deeper(self):
|
||||
"""
|
||||
Example 5: Even Deeper
|
||||
"""
|
||||
input_data = {
|
||||
'pet[0][species]': "Dahut",
|
||||
'pet[0][name]': "Hypatia",
|
||||
'pet[1][species]': "Felis Stultus",
|
||||
'pet[1][name]': "Billie",
|
||||
}
|
||||
expected_output = {
|
||||
'pet': [
|
||||
{
|
||||
'species': "Dahut",
|
||||
'name': "Hypatia",
|
||||
},
|
||||
{
|
||||
'species': "Felis Stultus",
|
||||
'name': "Billie",
|
||||
}
|
||||
]
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_such_deep(self):
|
||||
"""
|
||||
Example 6: Such Deep
|
||||
"""
|
||||
input_data = {
|
||||
'wow[such][deep][3][much][power][!]': "Amaze",
|
||||
}
|
||||
expected_output = {
|
||||
'wow': {
|
||||
'such': {
|
||||
'deep': [
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
{
|
||||
'much': {
|
||||
'power': {
|
||||
'!': "Amaze",
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_merge_behaviour(self):
|
||||
"""
|
||||
Example 7: Merge Behaviour
|
||||
"""
|
||||
# FIXME: Shouldn't this work regardless of key order?
|
||||
from rest_framework.compat import OrderedDict
|
||||
input_data = OrderedDict([
|
||||
('mix', "scalar"),
|
||||
('mix[0]', "array 1"),
|
||||
('mix[2]', "array 2"),
|
||||
('mix[key]', "key key"),
|
||||
('mix[car]', "car key"),
|
||||
])
|
||||
expected_output = {
|
||||
'mix': {
|
||||
'': "scalar",
|
||||
'0': "array 1",
|
||||
'2': "array 2",
|
||||
'key': "key key",
|
||||
'car': "car key",
|
||||
}
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
def test_append(self):
|
||||
"""
|
||||
Example 8: Append
|
||||
"""
|
||||
input_data = {
|
||||
'highlander[]': "one",
|
||||
}
|
||||
expected_output = {
|
||||
'highlander': ["one"],
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
||||
|
||||
# TODO: Example 9: Files
|
||||
|
||||
def test_invalid(self):
|
||||
"""
|
||||
Example 10: Invalid
|
||||
"""
|
||||
input_data = {
|
||||
"error[good]": "BOOM!",
|
||||
"error[bad": "BOOM BOOM!",
|
||||
}
|
||||
expected_output = {
|
||||
'error': {
|
||||
'good': "BOOM!",
|
||||
},
|
||||
'error[bad': "BOOM BOOM!",
|
||||
}
|
||||
result = html.parse_json_form(input_data)
|
||||
assert result == expected_output
|
Loading…
Reference in New Issue
Block a user