Skip to content

Commit 095e6c4

Browse files
authored
Merge branch 'main' into feat/mkdocs-material
2 parents 1f7feaa + 40e77f0 commit 095e6c4

File tree

7 files changed

+56
-21
lines changed

7 files changed

+56
-21
lines changed

docs/api-guide/fields.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, alth
417417

418418
## MultipleChoiceField
419419

420-
A field that can accept a set of zero, one or many values, chosen from a limited set of choices. Takes a single mandatory argument. `to_internal_value` returns a `set` containing the selected values.
420+
A field that can accept a list of zero, one or many values, chosen from a limited set of choices. Takes a single mandatory argument. `to_internal_value` returns a `list` containing the selected values, deduplicated.
421421

422422
**Signature:** `MultipleChoiceField(choices)`
423423

docs/tutorial/1-serialization.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Before we do anything else we'll create a new virtual environment, using [venv].
1515

1616
```bash
1717
python3 -m venv env
18-
source env/bin/activate
18+
source env/bin/activate # On Windows use `env\Scripts\activate`
1919
```
2020

2121
Now that we're inside a virtual environment, we can install our package requirements.
@@ -217,6 +217,10 @@ Let's look at refactoring our serializer using the `ModelSerializer` class.
217217
Open the file `snippets/serializers.py` again, and replace the `SnippetSerializer` class with the following.
218218

219219
```python
220+
from rest_framework import serializers
221+
from snippets.models import Snippet
222+
223+
220224
class SnippetSerializer(serializers.ModelSerializer):
221225
class Meta:
222226
model = Snippet
@@ -362,9 +366,9 @@ Quit the server with CONTROL-C.
362366

363367
In another terminal window, we can test the server.
364368

365-
We can test our API using [curl][curl] or [httpie][httpie]. Httpie is a user friendly http client that's written in Python. Let's install that.
369+
We can test our API using [curl][curl] or [HTTPie][HTTPie]. HTTPie is a user-friendly http client that's written in Python. Let's install that.
366370

367-
You can install httpie using pip:
371+
You can install HTTPie using pip:
368372

369373
```bash
370374
pip install httpie
@@ -436,5 +440,5 @@ We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
436440
[repo]: https://github.com/encode/rest-framework-tutorial
437441
[venv]: https://docs.python.org/3/library/venv.html
438442
[tut-2]: 2-requests-and-responses.md
439-
[httpie]: https://github.com/httpie/httpie#installation
443+
[HTTPie]: https://github.com/httpie/httpie#installation
440444
[curl]: https://curl.haxx.se/

rest_framework/fields.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,17 +1523,22 @@ def to_internal_value(self, data):
15231523
if not self.allow_empty and len(data) == 0:
15241524
self.fail('empty')
15251525

1526-
return {
1527-
# Arguments for super() are needed because of scoping inside
1528-
# comprehensions.
1529-
super(MultipleChoiceField, self).to_internal_value(item)
1530-
for item in data
1531-
}
1526+
# Arguments for super() are needed because of scoping inside
1527+
# comprehensions.
1528+
return list(
1529+
dict.fromkeys(
1530+
super(MultipleChoiceField, self).to_internal_value(item)
1531+
for item in data
1532+
)
1533+
)
15321534

15331535
def to_representation(self, value):
1534-
return {
1535-
self.choice_strings_to_values.get(str(item), item) for item in value
1536-
}
1536+
return list(
1537+
dict.fromkeys(
1538+
self.choice_strings_to_values.get(str(item), item)
1539+
for item in value
1540+
)
1541+
)
15371542

15381543

15391544
class FilePathField(ChoiceField):
76 Bytes
Binary file not shown.

rest_framework/locale/ru_RU/LC_MESSAGES/django.po

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ msgstr ""
5353

5454
#: authentication.py:203
5555
msgid "Invalid token."
56-
msgstr ""
56+
msgstr "Недействительный токен."
5757

5858
#: authtoken/apps.py:7
5959
msgid "Auth Token"

tests/test_fields.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
BuiltinSignatureError, DjangoImageField, SkipField, empty,
2525
is_simple_callable
2626
)
27+
from rest_framework.utils import json
2728
from tests.models import UUIDForeignKeyTarget
2829

2930
utc = datetime.timezone.utc
@@ -2178,16 +2179,20 @@ class TestMultipleChoiceField(FieldValues):
21782179
Valid and invalid values for `MultipleChoiceField`.
21792180
"""
21802181
valid_inputs = {
2181-
(): set(),
2182-
('aircon',): {'aircon'},
2183-
('aircon', 'manual'): {'aircon', 'manual'},
2182+
(): list(),
2183+
('aircon',): ['aircon'],
2184+
('aircon', 'aircon'): ['aircon'],
2185+
('aircon', 'manual'): ['aircon', 'manual'],
2186+
('manual', 'aircon'): ['manual', 'aircon'],
21842187
}
21852188
invalid_inputs = {
21862189
'abc': ['Expected a list of items but got type "str".'],
21872190
('aircon', 'incorrect'): ['"incorrect" is not a valid choice.']
21882191
}
21892192
outputs = [
2190-
(['aircon', 'manual', 'incorrect'], {'aircon', 'manual', 'incorrect'})
2193+
(['aircon', 'manual', 'incorrect'], ['aircon', 'manual', 'incorrect']),
2194+
(['manual', 'aircon', 'incorrect'], ['manual', 'aircon', 'incorrect']),
2195+
(['aircon', 'manual', 'aircon'], ['aircon', 'manual']),
21912196
]
21922197
field = serializers.MultipleChoiceField(
21932198
choices=[
@@ -2204,6 +2209,27 @@ def test_against_partial_and_full_updates(self):
22042209
field.partial = True
22052210
assert field.get_value(QueryDict('')) == rest_framework.fields.empty
22062211

2212+
def test_valid_inputs_is_json_serializable(self):
2213+
for input_value, _ in get_items(self.valid_inputs):
2214+
validated = self.field.run_validation(input_value)
2215+
2216+
try:
2217+
json.dumps(validated)
2218+
except TypeError as e:
2219+
pytest.fail(f'Validated output not JSON serializable: {repr(validated)}; Error: {e}')
2220+
2221+
def test_output_is_json_serializable(self):
2222+
for output_value, _ in get_items(self.outputs):
2223+
representation = self.field.to_representation(output_value)
2224+
2225+
try:
2226+
json.dumps(representation)
2227+
except TypeError as e:
2228+
pytest.fail(
2229+
f'to_representation output not JSON serializable: '
2230+
f'{repr(representation)}; Error: {e}'
2231+
)
2232+
22072233

22082234
class TestEmptyMultipleChoiceField(FieldValues):
22092235
"""

tests/test_serializer_nested.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ def test_nested_serializer_with_list_json(self):
199199
serializer = self.Serializer(data=input_data)
200200

201201
assert serializer.is_valid()
202-
assert serializer.validated_data['nested']['example'] == {1, 2}
202+
assert serializer.validated_data['nested']['example'] == [1, 2]
203203

204204
def test_nested_serializer_with_list_multipart(self):
205205
input_data = QueryDict('nested.example=1&nested.example=2')
206206
serializer = self.Serializer(data=input_data)
207207

208208
assert serializer.is_valid()
209-
assert serializer.validated_data['nested']['example'] == {1, 2}
209+
assert serializer.validated_data['nested']['example'] == [1, 2]
210210

211211

212212
class TestNotRequiredNestedSerializerWithMany:

0 commit comments

Comments
 (0)