Created
July 29, 2014 23:18
-
-
Save gavinwahl/17c07335c8dd1b832911 to your computer and use it in GitHub Desktop.
CheckedAtBooleanField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CheckedAtBooleanField(models.DateTimeField): | |
""" | |
A db field that acts like a checkbox in forms, but is backed by a | |
DateTimeField in the database. `None` represents that the box was | |
not checked, a timestamp value indicates that the box was checked | |
at that time. If the form is saved again, the time is not updated. | |
Specifically, the timestamp indicates when a user chose to opt in. | |
When setting the attribute on the model, it's possible to use the | |
values True and False. False sets the field to None. True behaves | |
like checkboxes do in a form -- The field is stores the timestamp | |
of the _first_ time it was True, not overriding the current value. | |
There are some decisions this field doesn't make -- How should it | |
behave in the admin center? Should it support filters like a bool? | |
Since querying like a boolean isn't supported, use isnull instead. | |
""" | |
def __init__(self, *args, **kwargs): | |
kwargs.setdefault('null', True) | |
kwargs.setdefault('blank', True) | |
super(CheckedAtBooleanField, self).__init__(*args, **kwargs) | |
def formfield(self, **kwargs): | |
# Should this be setitem or setdefault? It depends what admin | |
# should do. The admin will try to use a datetime widget here, | |
# so utilizing setitem instead of setdefault we override that. | |
kwargs['form_class'] = forms.BooleanField | |
kwargs['widget'] = forms.BooleanField.widget | |
return super(CheckedAtBooleanField, self).formfield(**kwargs) | |
def save_form_data(self, instance, data): | |
current_value = getattr(instance, self.attname) | |
if data: | |
data = current_value or timezone.now() | |
else: | |
data = None | |
return super(CheckedAtBooleanField, self).save_form_data(instance, data) | |
def contribute_to_class(self, cls, name): | |
super(CheckedAtBooleanField, self).contribute_to_class(cls, name) | |
setattr(cls, name, CheckedAtBooleanFieldDescriptor(self)) | |
class CheckedAtBooleanFieldDescriptor(object): | |
def __init__(self, field): | |
self.field = field | |
self.cache_name = self.field.get_cache_name() | |
def __get__(self, instance, instance_type=None): | |
if instance is None: | |
return self | |
else: | |
return getattr(instance, self.cache_name) | |
def __set__(self, instance, value): | |
if value is True: | |
current_value = getattr(instance, self.cache_name, None) | |
value = current_value or timezone.now() | |
elif value is False: | |
value = None | |
setattr(instance, self.cache_name, value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment