Skip to content

Commit 0c95405

Browse files
committed
Merge pull request encode#853 from pyriku/725-blank-choice-dash
Adds BLANK_CHOICE_DASH as a choice item
2 parents 2a3056d + 8fe4323 commit 0c95405

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed

rest_framework/fields.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from django.core import validators
1616
from django.core.exceptions import ValidationError
1717
from django.conf import settings
18+
from django.db.models.fields import BLANK_CHOICE_DASH
1819
from django import forms
1920
from django.forms import widgets
2021
from django.utils.encoding import is_protected_type
@@ -407,6 +408,8 @@ class ChoiceField(WritableField):
407408
def __init__(self, choices=(), *args, **kwargs):
408409
super(ChoiceField, self).__init__(*args, **kwargs)
409410
self.choices = choices
411+
if not self.required:
412+
self.choices = BLANK_CHOICE_DASH + self.choices
410413

411414
def _get_choices(self):
412415
return self._choices

rest_framework/serializers.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -705,15 +705,14 @@ def get_field(self, model_field):
705705
Creates a default instance of a basic non-relational field.
706706
"""
707707
kwargs = {}
708-
has_default = model_field.has_default()
709708

710-
if model_field.null or model_field.blank or has_default:
709+
if model_field.null or model_field.blank:
711710
kwargs['required'] = False
712711

713712
if isinstance(model_field, models.AutoField) or not model_field.editable:
714713
kwargs['read_only'] = True
715714

716-
if has_default:
715+
if model_field.has_default():
717716
kwargs['default'] = model_field.get_default()
718717

719718
if issubclass(model_field.__class__, models.TextField):

rest_framework/tests/fields.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,3 +659,29 @@ class DecimalSerializer(Serializer):
659659

660660
self.assertFalse(s.is_valid())
661661
self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 4 digits in total.']})
662+
663+
664+
class ChoiceFieldTests(TestCase):
665+
"""
666+
Tests for the ChoiceField options generator
667+
"""
668+
669+
SAMPLE_CHOICES = [
670+
('red', 'Red'),
671+
('green', 'Green'),
672+
('blue', 'Blue'),
673+
]
674+
675+
def test_choices_required(self):
676+
"""
677+
Make sure proper choices are rendered if field is required
678+
"""
679+
f = serializers.ChoiceField(required=True, choices=self.SAMPLE_CHOICES)
680+
self.assertEqual(f.choices, self.SAMPLE_CHOICES)
681+
682+
def test_choices_not_required(self):
683+
"""
684+
Make sure proper choices (plus blank) are rendered if the field isn't required
685+
"""
686+
f = serializers.ChoiceField(required=False, choices=self.SAMPLE_CHOICES)
687+
self.assertEqual(f.choices, models.fields.BLANK_CHOICE_DASH + self.SAMPLE_CHOICES)

rest_framework/tests/serializer.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from __future__ import unicode_literals
2+
from django.db import models
3+
from django.db.models.fields import BLANK_CHOICE_DASH
24
from django.utils.datastructures import MultiValueDict
35
from django.test import TestCase
46
from rest_framework import serializers
@@ -1001,6 +1003,73 @@ def test_serializer_data_is_pickleable(self):
10011003
repr(pickle.loads(pickle.dumps(data, 0)))
10021004

10031005

1006+
# test for issue #725
1007+
class SeveralChoicesModel(models.Model):
1008+
color = models.CharField(
1009+
max_length=10,
1010+
choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')],
1011+
blank=False
1012+
)
1013+
drink = models.CharField(
1014+
max_length=10,
1015+
choices=[('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')],
1016+
blank=False,
1017+
default='beer'
1018+
)
1019+
os = models.CharField(
1020+
max_length=10,
1021+
choices=[('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')],
1022+
blank=True
1023+
)
1024+
music_genre = models.CharField(
1025+
max_length=10,
1026+
choices=[('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')],
1027+
blank=True,
1028+
default='metal'
1029+
)
1030+
1031+
1032+
class SerializerChoiceFields(TestCase):
1033+
1034+
def setUp(self):
1035+
super(SerializerChoiceFields, self).setUp()
1036+
1037+
class SeveralChoicesSerializer(serializers.ModelSerializer):
1038+
class Meta:
1039+
model = SeveralChoicesModel
1040+
fields = ('color', 'drink', 'os', 'music_genre')
1041+
1042+
self.several_choices_serializer = SeveralChoicesSerializer
1043+
1044+
def test_choices_blank_false_not_default(self):
1045+
serializer = self.several_choices_serializer()
1046+
self.assertEqual(
1047+
serializer.fields['color'].choices,
1048+
[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')]
1049+
)
1050+
1051+
def test_choices_blank_false_with_default(self):
1052+
serializer = self.several_choices_serializer()
1053+
self.assertEqual(
1054+
serializer.fields['drink'].choices,
1055+
[('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')]
1056+
)
1057+
1058+
def test_choices_blank_true_not_default(self):
1059+
serializer = self.several_choices_serializer()
1060+
self.assertEqual(
1061+
serializer.fields['os'].choices,
1062+
BLANK_CHOICE_DASH + [('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')]
1063+
)
1064+
1065+
def test_choices_blank_true_with_default(self):
1066+
serializer = self.several_choices_serializer()
1067+
self.assertEqual(
1068+
serializer.fields['music_genre'].choices,
1069+
BLANK_CHOICE_DASH + [('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')]
1070+
)
1071+
1072+
10041073
class DepthTest(TestCase):
10051074
def test_implicit_nesting(self):
10061075

0 commit comments

Comments
 (0)