toastdriven/django-tastypie — pull request #271 by ryankask (2011-10-20 09:35:27+00:00)
Title: Fields with null=True should dehydrate to None before returning the default

I previously [submitted this issue](https://github.com/toastdriven/django-tastypie/pull/269) but closed it because I wanted to refactor the tests.

The current tests located in ``tests.core.fields.ApiFieldTestCase.test_dehydrate`` test for a lot of conditions, but not when a field has ``null=True`` and ``default=True``.

The logic in ``fields.ApiField.dehydrate`` specifies that if the field has a ``default`` then it takes precedence over null (``None``). This means that if you have a Django model like

class Comment(models.Model):
times_viewed = models.IntegerField(null=True, default=0)

and an object with ``times_viewed=None``, ``ApiField.dehydrate`` will return the default, 0, instead of None.

My patch inverts the checks for a default and null. The default will only be returned if ``null=False``.

One issue that I am making an assumption about has to do with when ``ApiField.attribute`` is wrong or missing. This is [tested](https://github.com/ryankask/django-tastypie/blob/11a5adb087c4d0e80c6082089a2f5ffc49f8ad72/tests/core/tests/fields.py#L38) for a bunch of times but never when ``null=True`` *and* a ``default`` is set. I didn't think about this the first time I posted this issue. This is the [relevent line](https://github.com/ryankask/django-tastypie/blob/11a5adb087c4d0e80c6082089a2f5ffc49f8ad72/tastypie/fields.py#L94):

current_object = getattr(current_object, attr, None)

My patch's ``dehydrate`` will return ``None`` (null). I chose this because I think if null is allowed, it should take precedence over a default. I personally think ``default`` should only matter for the creation of objects, which matches Django. A default *may* be useful when working with plain resources but I haven't needed it for GET requests.

One alternative would be to return the default if ``ApiField.attribute`` is missing or wrong. The code would have to stop conflating ``None`` as the absence of a value on a valid attribute and ``None`` returned ``getattr`` because of an ``AttributeError``. Something like:

try:
current_object = getattr(current_object, attr)
except AttributeError:
use_default = True
else:
use_default = False

and then update the logic below accordingly.

This issue shouldn't change the fact that if the database stores null, then a field with ``default`` set shouldn't override that; ``None`` should be returned.