Navigation Menu

Skip to content

Commit

Permalink
Added compatibility for Django 1.4's time zone awareness. This also u…
Browse files Browse the repository at this point in the history
…pdates the tox configuration quite a bit to make it a little bit more flexible. Also drop 1.2.X since the tests already weren't passing there and 1.2.X is about to get dropped from Django core support.
  • Loading branch information
jezdez authored and toastdriven committed Jan 17, 2012
1 parent 498afa6 commit 4c1dfb0
Show file tree
Hide file tree
Showing 21 changed files with 173 additions and 97 deletions.
4 changes: 3 additions & 1 deletion docs/cookbook.rst
Expand Up @@ -29,12 +29,14 @@ A common pattern is needing to limit a queryset by something that changes
per-request, for instance the date/time. You can accomplish this by lightly
modifying ``get_object_list``::

from tastypie.utils import now

class MyResource(ModelResource):
class Meta:
queryset = MyObject.objects.all()

def get_object_list(self, request):
return super(MyResource, self).get_object_list(request).filter(start_date__gte=datetime.datetime.now)
return super(MyResource, self).get_object_list(request).filter(start_date__gte=now)


Using Your ``Resource`` In Regular Views
Expand Down
5 changes: 2 additions & 3 deletions docs/fields.rst
Expand Up @@ -15,16 +15,15 @@ Quick Start

For the impatient::

import datetime
from tastypie import fields
from tastypie import fields, utils
from tastypie.resources import Resource
from myapp.api.resources import ProfileResource, NoteResource


class PersonResource(Resource):
name = fields.CharField(attribute='name')
age = fields.IntegerField(attribute='years_old', null=True)
created = fields.DateTimeField(readonly=True, help_text='When the person was created', default=datetime.datetime.now)
created = fields.DateTimeField(readonly=True, help_text='When the person was created', default=utils.now)
is_active = fields.BooleanField(default=True)
profile = fields.ToOneField(ProfileResource, 'profile')
notes = fields.ToManyField(NoteResource, 'notes', full=True)
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial.rst
Expand Up @@ -25,15 +25,15 @@ of the code that are Tastypie-specific in any kind of depth.
For example purposes, we'll be adding an API to a simple blog application.
Here is ``myapp/models.py``::

import datetime
from tastypie.utils import now
from django.contrib.auth.models import User
from django.db import models
from django.template.defaultfilters import slugify
class Entry(models.Model):
user = models.ForeignKey(User)
pub_date = models.DateTimeField(default=datetime.datetime.now)
pub_date = models.DateTimeField(default=now)
title = models.CharField(max_length=200)
slug = models.SlugField()
body = models.TextField()
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Expand Up @@ -3,5 +3,5 @@ mimeparse>=0.1.3
python-dateutil==1.5
lxml
PyYAML
python_digest
python-digest
biplist
8 changes: 4 additions & 4 deletions tastypie/fields.py
Expand Up @@ -6,7 +6,7 @@
from django.utils import datetime_safe, importlib
from tastypie.bundle import Bundle
from tastypie.exceptions import ApiFieldError, NotFound
from tastypie.utils import dict_strip_unicode_keys
from tastypie.utils import dict_strip_unicode_keys, make_aware


class NOT_PROVIDED:
Expand Down Expand Up @@ -322,7 +322,7 @@ def hydrate(self, bundle):
if value and not hasattr(value, 'year'):
try:
# Try to rip a date/datetime out of it.
value = parse(value)
value = make_aware(parse(value))

if hasattr(value, 'hour'):
value = value.date()
Expand All @@ -348,7 +348,7 @@ def convert(self, value):

if match:
data = match.groupdict()
return datetime_safe.datetime(int(data['year']), int(data['month']), int(data['day']), int(data['hour']), int(data['minute']), int(data['second']))
return make_aware(datetime_safe.datetime(int(data['year']), int(data['month']), int(data['day']), int(data['hour']), int(data['minute']), int(data['second'])))
else:
raise ApiFieldError("Datetime provided to '%s' field doesn't appear to be a valid datetime string: '%s'" % (self.instance_name, value))

Expand All @@ -360,7 +360,7 @@ def hydrate(self, bundle):
if value and not hasattr(value, 'year'):
try:
# Try to rip a date/datetime out of it.
value = parse(value)
value = make_aware(parse(value))
except ValueError:
pass

Expand Down
10 changes: 6 additions & 4 deletions tastypie/migrations/0001_initial.py
Expand Up @@ -4,6 +4,8 @@
from south.v2 import SchemaMigration
from django.db import models

from tastypie.utils import now

class Migration(SchemaMigration):

def forwards(self, orm):
Expand All @@ -23,7 +25,7 @@ def forwards(self, orm):
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.OneToOneField')(related_name='api_key', unique=True, to=orm['auth.User'])),
('key', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
('created', self.gf('django.db.models.fields.DateTimeField')(default=now)),
))
db.send_create_signal('tastypie', ['ApiKey'])

Expand Down Expand Up @@ -53,15 +55,15 @@ def backwards(self, orm):
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'tastypie.utils.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'tastypie.utils.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
Expand All @@ -84,7 +86,7 @@ def backwards(self, orm):
},
'tastypie.apikey': {
'Meta': {'object_name': 'ApiKey'},
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'tastypie.utils.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'api_key'", 'unique': 'True', 'to': "orm['auth.User']"})
Expand Down
6 changes: 4 additions & 2 deletions tastypie/models.py
Expand Up @@ -3,6 +3,8 @@
import time
from django.conf import settings
from django.db import models
from tastypie.utils import now

try:
from hashlib import sha1
except ImportError:
Expand Down Expand Up @@ -33,8 +35,8 @@ def save(self, *args, **kwargs):
class ApiKey(models.Model):
user = models.OneToOneField(User, related_name='api_key')
key = models.CharField(max_length=256, blank=True, default='')
created = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)

def __unicode__(self):
return u"%s for %s" % (self.key, self.user)

Expand Down
1 change: 0 additions & 1 deletion tastypie/resources.py
Expand Up @@ -2008,7 +2008,6 @@ def convert_post_to_VERB(request, verb):
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = verb

setattr(request, verb, request.POST)

return request
Expand Down
3 changes: 2 additions & 1 deletion tastypie/serializers.py
Expand Up @@ -7,7 +7,7 @@
from django.utils.encoding import force_unicode
from tastypie.bundle import Bundle
from tastypie.exceptions import UnsupportedFormat
from tastypie.utils import format_datetime, format_date, format_time
from tastypie.utils import format_datetime, format_date, format_time, make_naive
try:
import lxml
from lxml.etree import parse as parse_xml
Expand Down Expand Up @@ -120,6 +120,7 @@ def format_datetime(self, data):
Default is ``iso-8601``, which looks like "2010-12-16T03:02:14".
"""
data = make_naive(data)
if self.datetime_formatting == 'rfc-2822':
return format_datetime(data)

Expand Down
1 change: 1 addition & 0 deletions tastypie/utils/__init__.py
Expand Up @@ -2,3 +2,4 @@
from tastypie.utils.formatting import mk_datetime, format_datetime, format_date, format_time
from tastypie.utils.urls import trailing_slash
from tastypie.utils.validate_jsonp import is_valid_jsonp_callback_value
from tastypie.utils.timezone import now, make_aware, make_naive, aware_date, aware_datetime
9 changes: 5 additions & 4 deletions tastypie/utils/formatting.py
@@ -1,34 +1,35 @@
import email
import datetime
from django.utils import dateformat
from tastypie.utils.timezone import make_aware, make_naive, aware_datetime

# Try to use dateutil for maximum date-parsing niceness. Fall back to
# hard-coded RFC2822 parsing if that's not possible.
try:
from dateutil.parser import parse as mk_datetime
except ImportError:
def mk_datetime(string):
return datetime.datetime.fromtimestamp(time.mktime(email.utils.parsedate(string)))
return make_aware(datetime.datetime.fromtimestamp(time.mktime(email.utils.parsedate(string))))

def format_datetime(dt):
"""
RFC 2822 datetime formatter
"""
return dateformat.format(dt, 'r')
return dateformat.format(make_naive(dt), 'r')

def format_date(d):
"""
RFC 2822 date formatter
"""
# workaround because Django's dateformat utility requires a datetime
# object (not just date)
dt = datetime.datetime(d.year, d.month, d.day, 0, 0, 0)
dt = aware_datetime(d.year, d.month, d.day, 0, 0, 0)
return dateformat.format(dt, 'j M Y')

def format_time(t):
"""
RFC 2822 time formatter
"""
# again, workaround dateformat input requirement
dt = datetime.datetime(2000, 1, 1, t.hour, t.minute, t.second)
dt = aware_datetime(2000, 1, 1, t.hour, t.minute, t.second)
return dateformat.format(dt, 'H:i:s O')
30 changes: 30 additions & 0 deletions tastypie/utils/timezone.py
@@ -0,0 +1,30 @@
import datetime
from django.conf import settings

try:
from django.utils import timezone

def make_aware(value):
if getattr(settings, "USE_TZ", False):
default_tz = timezone.get_default_timezone()
value = timezone.make_aware(value, default_tz)
return value

def make_naive(value):
if getattr(settings, "USE_TZ", False) and timezone.is_aware(value):
default_tz = timezone.get_default_timezone()
value = timezone.make_naive(value, default_tz)
return value

def now():
return timezone.localtime(timezone.now())

except ImportError:
now = datetime.datetime.now
make_aware = make_naive = lambda x: x

def aware_date(*args, **kwargs):
return make_aware(datetime.date(*args, **kwargs))

def aware_datetime(*args, **kwargs):
return make_aware(datetime.datetime(*args, **kwargs))
11 changes: 6 additions & 5 deletions tests/alphanumeric/models.py
@@ -1,16 +1,17 @@
import datetime
from django.db import models
from tastypie.utils import now


class Product(models.Model):
artnr = models.CharField(max_length=8, primary_key=True)
name = models.CharField(max_length=32, null=False, blank=True, default='')
created = models.DateTimeField(default=datetime.datetime.now)
updated = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)
updated = models.DateTimeField(default=now)

def __unicode__(self):
return "%s - %s" % (self.artnr, self.name)

def save(self, *args, **kwargs):
self.updated = datetime.datetime.now()
self.updated = now()
return super(Product, self).save(*args, **kwargs)
10 changes: 5 additions & 5 deletions tests/basic/models.py
@@ -1,22 +1,22 @@
import datetime
from django.contrib.auth.models import User
from django.db import models

from tastypie.utils import now

class Note(models.Model):
user = models.ForeignKey(User, related_name='notes')
title = models.CharField(max_length=255)
slug = models.SlugField()
content = models.TextField()
is_active = models.BooleanField(default=True)
created = models.DateTimeField(default=datetime.datetime.now)
updated = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)
updated = models.DateTimeField(default=now)

def __unicode__(self):
return self.title

def save(self, *args, **kwargs):
self.updated = datetime.datetime.now()
self.updated = now()
return super(Note, self).save(*args, **kwargs)

class AnnotatedNote(models.Model):
Expand Down
8 changes: 5 additions & 3 deletions tests/complex/models.py
Expand Up @@ -9,22 +9,24 @@
from django.db.models import signals, get_models
from django.conf import settings

from tastypie.utils import now


class Post(models.Model):
user = models.ForeignKey(User, related_name='notes')
title = models.CharField(max_length=255)
slug = models.SlugField()
content = models.TextField()
is_active = models.BooleanField(default=True)
created = models.DateTimeField(default=datetime.datetime.now)
updated = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)
updated = models.DateTimeField(default=now)
comments = generic.GenericRelation(Comment, content_type_field="content_type", object_id_field="object_pk")

def __unicode__(self):
return self.title

def save(self, *args, **kwargs):
self.updated = datetime.datetime.now()
self.updated = now()
return super(Post, self).save(*args, **kwargs)


Expand Down
17 changes: 9 additions & 8 deletions tests/core/models.py
@@ -1,6 +1,7 @@
import datetime
from django.contrib.auth.models import User
from django.db import models
from tastypie.utils import now, aware_datetime


class Note(models.Model):
Expand All @@ -9,19 +10,19 @@ class Note(models.Model):
slug = models.SlugField()
content = models.TextField(blank=True)
is_active = models.BooleanField(default=True)
created = models.DateTimeField(default=datetime.datetime.now)
updated = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)
updated = models.DateTimeField(default=now)

def __unicode__(self):
return self.title

def save(self, *args, **kwargs):
self.updated = datetime.datetime.now()
self.updated = now()
return super(Note, self).save(*args, **kwargs)

def what_time_is_it(self):
return datetime.datetime(2010, 4, 1, 0, 48)
return aware_datetime(2010, 4, 1, 0, 48)

def get_absolute_url(self):
return '/some/fake/path/%s/' % self.pk

Expand All @@ -34,8 +35,8 @@ class Subject(models.Model):
notes = models.ManyToManyField(Note, related_name='subjects')
name = models.CharField(max_length=255)
url = models.URLField(verify_exists=False)
created = models.DateTimeField(default=datetime.datetime.now)
created = models.DateTimeField(default=now)

def __unicode__(self):
return self.name

Expand Down

0 comments on commit 4c1dfb0

Please sign in to comment.