A vanilla ModelSerializer does not inherit the model field default values unless db_index=true, which is okay for a POST request with JSON iff Content-Type header is set to application/json.
But if the the content type is not set, then if the field is not in the body of the POST, and the field is a boolean, then data is set to
default_empty_html which is "false".
Is this expected?
I apologize if this is the expected behavior!
Also I didn't try this with the newest versions of Django or DRF.
Versions:
rest_framework==3.5.4
django==1.10
python==2.7.13
Here is a vanilla django model with 2 fields, both have defaults, but one is indexed (?):
class MyModel(models.Model):
field1 = models.BooleanField(
"indexed boolean field", default=False, db_index=True
)
field2 = models.BooleanField(default=True) # not indexed, no label
field3 = models.CharField('blah', max_length=100)
And a vanilla DRF model serializer
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
If I serialize a dictionary in the django shell, and call the serializer's
save() method, the correct defaults are used
from myapp.serializers import MyModelSerializer
x = {'field3': 'blah blah blah'}
s = MyModelSerializer(data=x)
s.is_valid()
o = s.save()
assert o.field1 # OK!
assert o.field2 # OK!
but if I use a test client, then it doesn't return the correct defaults
from rest_framework.test import APIClient
from django.contrib.auth.models import User
from django.conf import settings
settings.ALLOWED_HOSTS.append(u'testserver')
me = User.objects.get(username='me')
client = APIClient()
client.force_authenticate(user=me)
r =
client.post('/mymodel/', x)
o = r.json()
assert o['field1'] # OK!
assert o['field2'] # FAILS!
if I use the test client with the correct content type, now it works again!
r = client.post('/mymodel/', x, format='json') # it's JSON not HTML! o = r.json()
assert o['field1'] # OK!
assert o['field2'] # OK!
why? If I look at the fields in the serializer, only the indexed fields have defaults, some of the fields have labels even though they aren't explicitly defined in the model
s.fields
{
'field1': BooleanField(default=True, label='indexed boolean field', required=False),
'field2': BooleanField(label='field2', required=False),
'field3': CharField(label='blah', max_length=100)
}
And the serializer fields are treated differently depending on whether or not they are assumed be HTML based on a call to
rest_framework.utils.html.is_html_input which returns true if the data has the
getlist attribute.
If the data is JSON or a Python dictionary, it doesn't have
getlist, so
is_html_input returns
False and the field's
get_value(data) call returns the
empty field.
But if the data is posted without the JSON content type header, then since the field has no default value, the default_empty_html value is returned instead, which is False for boolean fields.
:-(
I guess the moral of the story is make sure you are using the correct content type in your POST request, or you never know what you might get? If anyone has any response that will make me feel less crazy, I would appreciate it.
Thanks! And thanks Tom Christie and all of the other maintainers for DRF. I love it!
:-)