NewsBlur/vendor/timezones/fields.py

159 lines
6.3 KiB
Python
Executable file

from django.conf import settings
from django.db import models
from django.db.models import signals
from django.utils.encoding import smart_unicode, smart_str
import pytz
from vendor.timezones import forms, zones
from vendor.timezones.utilities import coerce_timezone_value, validate_timezone_max_length
MAX_TIMEZONE_LENGTH = getattr(settings, "MAX_TIMEZONE_LENGTH", 100)
default_tz = pytz.timezone(getattr(settings, "TIME_ZONE", "UTC"))
class TimeZoneField(models.CharField):
def __init__(self, *args, **kwargs):
validate_timezone_max_length(MAX_TIMEZONE_LENGTH, zones.ALL_TIMEZONE_CHOICES)
defaults = {
"max_length": MAX_TIMEZONE_LENGTH,
"default": settings.TIME_ZONE,
"choices": zones.PRETTY_TIMEZONE_CHOICES
}
defaults.update(kwargs)
return super(TimeZoneField, self).__init__(*args, **defaults)
def validate(self, value, model_instance):
# coerce value back to a string to validate correctly
return super(TimeZoneField, self).validate(smart_str(value), model_instance)
def run_validators(self, value):
# coerce value back to a string to validate correctly
return super(TimeZoneField, self).run_validators(smart_str(value))
def from_db_value(self, value, expression, connection, context):
value = super(TimeZoneField, self).to_python(value)
if value is None:
return None # null=True
return coerce_timezone_value(value)
def to_python(self, value):
value = super(TimeZoneField, self).to_python(value)
if value is None:
return None # null=True
return coerce_timezone_value(value)
def get_prep_value(self, value):
if value is not None:
return smart_unicode(value)
return value
def get_db_prep_save(self, value, connection=None):
"""
Prepares the given value for insertion into the database.
"""
return self.get_prep_value(value)
def flatten_data(self, follow, obj=None):
value = self.value_from_object(obj)
if value is None:
value = ""
return {self.attname: smart_unicode(value)}
class LocalizedDateTimeField(models.DateTimeField):
"""
A model field that provides automatic localized timezone support.
timezone can be a timezone string, a callable (returning a timezone string),
or a queryset keyword relation for the model, or a pytz.timezone()
result.
"""
def __init__(self, verbose_name=None, name=None, timezone=None, **kwargs):
if isinstance(timezone, basestring):
timezone = smart_str(timezone)
if timezone in pytz.all_timezones_set:
self.timezone = pytz.timezone(timezone)
else:
self.timezone = timezone
super(LocalizedDateTimeField, self).__init__(verbose_name, name, **kwargs)
def formfield(self, **kwargs):
defaults = {"form_class": forms.LocalizedDateTimeField}
if (not isinstance(self.timezone, basestring) and str(self.timezone) in pytz.all_timezones_set):
defaults["timezone"] = str(self.timezone)
defaults.update(kwargs)
return super(LocalizedDateTimeField, self).formfield(**defaults)
def get_db_prep_save(self, value, connection=None):
"""
Returns field's value prepared for saving into a database.
"""
## convert to settings.TIME_ZONE
if value is not None:
if value.tzinfo is None:
value = default_tz.localize(value)
else:
value = value.astimezone(default_tz)
return super(LocalizedDateTimeField, self).get_db_prep_save(value, connection=connection)
def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=None):
"""
Returns field's value prepared for database lookup.
"""
## convert to settings.TIME_ZONE
if value.tzinfo is None:
value = default_tz.localize(value)
else:
value = value.astimezone(default_tz)
return super(LocalizedDateTimeField, self).get_db_prep_lookup(lookup_type, value, connection=connection, prepared=prepared)
def prep_localized_datetime(sender, **kwargs):
for field in sender._meta.fields:
if not isinstance(field, LocalizedDateTimeField) or field.timezone is None:
continue
dt_field_name = "_datetimezone_%s" % field.attname
def get_dtz_field(instance):
return getattr(instance, dt_field_name)
def set_dtz_field(instance, dt):
if dt.tzinfo is None:
dt = default_tz.localize(dt)
time_zone = field.timezone
if isinstance(field.timezone, basestring):
tz_name = instance._default_manager.filter(
pk=model_instance._get_pk_val()
).values_list(field.timezone)[0][0]
try:
time_zone = pytz.timezone(tz_name)
except:
time_zone = default_tz
if time_zone is None:
# lookup failed
time_zone = default_tz
#raise pytz.UnknownTimeZoneError(
# "Time zone %r from relation %r was not found"
# % (tz_name, field.timezone)
#)
elif callable(time_zone):
tz_name = time_zone()
if isinstance(tz_name, basestring):
try:
time_zone = pytz.timezone(tz_name)
except:
time_zone = default_tz
else:
time_zone = tz_name
if time_zone is None:
# lookup failed
time_zone = default_tz
#raise pytz.UnknownTimeZoneError(
# "Time zone %r from callable %r was not found"
# % (tz_name, field.timezone)
#)
setattr(instance, dt_field_name, dt.astimezone(time_zone))
setattr(sender, field.attname, property(get_dtz_field, set_dtz_field))
## RED_FLAG: need to add a check at manage.py validation time that
## time_zone value is a valid query keyword (if it is one)
signals.class_prepared.connect(prep_localized_datetime)