get_next_by_FOO allows nulls
|Reported by:||CarlFK||Owned by:||nobody|
|Component:||Database layer (models, ORM)||Version:||master|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
"For every DateField and DateTimeField that does not have null=True, the object will have get_next_by_FOO()"
which is correct given this line of code:
if not self.null:
I have a need for this to work on a nullable field, so I removed the restriction. The code change is fairly trivial, documenting the resulting behavior was the hard part, but after some lengthy IRC debates that included looking at what the sql97 spec on how nulls were handled by order_by, we came to a consensus.
Docs: remove “that does not have null=True” add: "There are 2 groups of rows that can be iterated over using .get_next/previous: those with values and those with nulls. Going beyond the edge of either set raises DoesNotExist." That is all that needs to be documented. This does not explain how nulls and non nulls are related: they aren't. Which doesn't explain anything. Here are my thoughts: If the application developer wants the first null to come after the last value, they will have to code that in their app. Or vice-versa. The app developer should be given the choice to treat the 2 sets as one continuum or not, and be given the choice of nulls coming before or after the values.
How I use it:
try: prev_episode = episode.get_previous_by_start(state=episode.state) except Episode.DoesNotExist: # at edge of the set of nulls or values. # In this app, *nulls come before values*. if episode.start is not None: # we are at the first value, try to go to the last null try: prev_episode = Episode.objects.filter(start__isnull=True).order_by('id').reverse() except Episode.DoesNotExist: # There are no nulls prev_episode = None else: # there is no privious null, we have nowhere to go. prev_episode = None
There is a similar block for next_episode=episode.get_next which can be seen in my app: http://github.com/CarlFK/veyepar/blob/master/dj/main/views.py#L366