Created a new serializer field NullBooleanField to fix #1422. This field

is used as the default for Django's `NullBooleanField` in `ModelSerializer`s; it
differs from `BooleanField` in that it is nullable and therefore allows `None`
values.
This commit is contained in:
Darian Moody 2014-02-16 22:15:31 +00:00
parent d4b9db2946
commit 83f0cb574d
3 changed files with 81 additions and 40 deletions

View File

@ -453,6 +453,26 @@ class BooleanField(WritableField):
return bool(value)
class NullBooleanField(WritableField):
type_name = 'NullBooleanField'
type_label = 'null boolean'
form_field_class = forms.NullBooleanField
widget = widgets.NullBooleanSelect
default_error_messages = {
'invalid': _("'%s' value must be either None, True or False."),
}
empty = None
def from_native(self, value):
if value is None:
return None
if value in ('true', 't', 'True', '1'):
return True
if value in ('false', 'f', 'False', '0'):
return False
return bool(value)
class CharField(WritableField):
type_name = 'CharField'
type_label = 'string'

View File

@ -648,7 +648,7 @@ class ModelSerializer(Serializer):
models.TextField: CharField,
models.CommaSeparatedIntegerField: CharField,
models.BooleanField: BooleanField,
models.NullBooleanField: BooleanField,
models.NullBooleanField: NullBooleanField,
models.FileField: FileField,
models.ImageField: ImageField,
}

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django import forms
from django.db import models
from django.db.models.fields import BLANK_CHOICE_DASH
from django.test import TestCase
@ -1847,19 +1848,12 @@ class BooleanFieldTypeTest(TestCase):
bfield = self.serializer.get_fields()['done']
self.assertEqual(type(bfield), fields.BooleanField)
def test_nullbooleanfield_type(self):
'''
Test that BooleanField is infered from models.NullBooleanField
https://groups.google.com/forum/#!topic/django-rest-framework/D9mXEftpuQ8
'''
bfield = self.serializer.get_fields()['started']
self.assertEqual(type(bfield), fields.BooleanField)
class NullBooleanFieldModel(models.Model):
"""
A model for use in the NullBooleanFieldSerializer test.
A model for use in the NullBooleanFieldSerializer test. Showcases
all variations of the 'default' parameter (which is allowed to be
unset unlike models.BooleanField.
"""
cat = models.NullBooleanField(default=True)
dog = models.NullBooleanField(default=False)
@ -1881,43 +1875,51 @@ class NullBooleanFieldSerializerTest(TestCase):
fields = ('cat', 'dog', 'rat')
self.serializer = NullBooleanFieldSerializer
# For ease we save a reference to the fields we need.
self.fields = self.serializer.Meta.fields
def test_false(self):
data = {
'cat': False,
'dog': False,
'rat': False
}
serializer = self.serializer(data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], False)
self.assertEqual(serializer.data['dog'], False)
self.assertEqual(serializer.data['rat'], False)
"""
Check that all 'falsy' inputs result in the correct output
to the data dictionary across all our variations of NullBooleanField.
"""
falsy_values = ['false', 'f', 'False', '0', False, 0, '']
for val in falsy_values:
# For each falsy value, we create a data dictionary where all
# fields are set to that falsy value and then check for the
# desired serializer output: False.
data = dict([(field, val) for field in self.fields])
serializer = self.serializer(data=data)
self.assertEqual(serializer.is_valid(), True)
for field in self.fields:
self.assertEqual(serializer.data[field], False)
def test_true(self):
data = {
'cat': True,
'dog': True,
'rat': True
}
serializer = self.serializer(data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], True)
self.assertEqual(serializer.data['dog'], True)
self.assertEqual(serializer.data['rat'], True)
"""
Check that all 'truthy' inputs result in the correct output
to the data dictionary across all our variations of NullBooleanField.
"""
truthy_values = ['true', 't', 'True', '1', True, 1, 'truthy-string']
for val in truthy_values:
# For each truthy value, we create a data dictionary where all
# fields are set to that falsy value and then check for the
# desired serializer output: True.
data = dict([(field, val) for field in self.fields])
serializer = self.serializer(data=data)
self.assertEqual(serializer.is_valid(), True)
for field in self.fields:
self.assertEqual(serializer.data[field], True)
def test_none(self):
data = {
'cat': None,
'dog': None,
'rat': None
}
"""
Check that all 'None' input result in the correct output
to the data dictionary across all our variations of NullBooleanField.
"""
data = dict([(field, None) for field in self.fields])
serializer = self.serializer(data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], None)
self.assertEqual(serializer.data['dog'], None)
self.assertEqual(serializer.data['rat'], None)
for field in self.fields:
self.assertEqual(serializer.data[field], None)
def test_partial(self):
serializer = self.serializer(data={'rat': None}, partial=True)
@ -1925,3 +1927,22 @@ class NullBooleanFieldSerializerTest(TestCase):
self.assertEqual(serializer.data['cat'], True)
self.assertEqual(serializer.data['dog'], False)
self.assertEqual(serializer.data['rat'], None)
def test_nullbooleanfield_type(self):
"""
Test that DRF's NullBooleanField is selected by default for
Django's NullBooleanFields.
"""
serializer = self.serializer()
for field in self.fields:
bfield = serializer.get_fields()[field]
self.assertEqual(type(bfield), fields.NullBooleanField)
def test_nullbooleanfield_widget_type(self):
"""
Test that Django's NullBooleanSelect widget is used as the default
widget for DRF's NullBooleanField.
"""
serializer = self.serializer()
for field_name, field in serializer.fields.iteritems():
self.assertEqual(type(field.widget), forms.NullBooleanSelect)