Fixed #10497 -- Added a few time-related methods to the storage API. Thanks for the report and patch to Stephan Jaekel.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14012 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-10-08 15:11:59 +00:00
parent 4d4d68a2cd
commit 58b704d8da
3 changed files with 114 additions and 5 deletions

View File

@ -2,6 +2,7 @@ import os
import errno import errno
import urlparse import urlparse
import itertools import itertools
from datetime import datetime
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
@ -120,6 +121,27 @@ class Storage(object):
""" """
raise NotImplementedError() raise NotImplementedError()
def accessed_time(self, name):
"""
Returns the last accessed time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
def created_time(self, name):
"""
Returns the creation time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
def modified_time(self, name):
"""
Returns the last modified time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
class FileSystemStorage(Storage): class FileSystemStorage(Storage):
""" """
Standard filesystem storage Standard filesystem storage
@ -220,6 +242,15 @@ class FileSystemStorage(Storage):
raise ValueError("This file is not accessible via a URL.") raise ValueError("This file is not accessible via a URL.")
return urlparse.urljoin(self.base_url, name).replace('\\', '/') return urlparse.urljoin(self.base_url, name).replace('\\', '/')
def accessed_time(self, name):
return datetime.fromtimestamp(os.path.getatime(self.path(name)))
def created_time(self, name):
return datetime.fromtimestamp(os.path.getctime(self.path(name)))
def modified_time(self, name):
return datetime.fromtimestamp(os.path.getmtime(self.path(name)))
def get_storage_class(import_path=None): def get_storage_class(import_path=None):
if import_path is None: if import_path is None:
import_path = settings.DEFAULT_FILE_STORAGE import_path = settings.DEFAULT_FILE_STORAGE

View File

@ -13,6 +13,33 @@ The local filesystem path where the file can be opened using Python's standard
``open()``. For storage systems that aren't accessible from the local ``open()``. For storage systems that aren't accessible from the local
filesystem, this will raise ``NotImplementedError`` instead. filesystem, this will raise ``NotImplementedError`` instead.
``Storage.accessed_time(name)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.3
Returns a ``datetime`` object containing the last accessed time of the file.
For storage systems that aren't able to return the last accessed time, this
will raise ``NotImplementedError`` instead.
``Storage.created_time(name)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.3
Returns a ``datetime`` object containing the creation time of the file.
For storage systems that aren't able to return the creation time, this
will raise ``NotImplementedError`` instead.
``Storage.modified_time(name)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.3
Returns a ``datetime`` object containing the last modified time. For storage
systems that aren't able to return the last modified time, this will raise
``NotImplementedError`` instead.
``Storage.size(name)`` ``Storage.size(name)``
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~

View File

@ -6,6 +6,7 @@ import tempfile
import time import time
import unittest import unittest
from cStringIO import StringIO from cStringIO import StringIO
from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.core.files.base import ContentFile, File from django.core.files.base import ContentFile, File
@ -81,16 +82,16 @@ class GetStorageClassTests(unittest.TestCase):
class FileStorageTests(unittest.TestCase): class FileStorageTests(unittest.TestCase):
storage_class = FileSystemStorage storage_class = FileSystemStorage
def setUp(self): def setUp(self):
self.temp_dir = tempfile.mktemp() self.temp_dir = tempfile.mktemp()
os.makedirs(self.temp_dir) os.makedirs(self.temp_dir)
self.storage = self.storage_class(location=self.temp_dir, self.storage = self.storage_class(location=self.temp_dir,
base_url='/test_media_url/') base_url='/test_media_url/')
def tearDown(self): def tearDown(self):
shutil.rmtree(self.temp_dir) shutil.rmtree(self.temp_dir)
def test_file_access_options(self): def test_file_access_options(self):
""" """
Standard file access options are available, and work as expected. Standard file access options are available, and work as expected.
@ -104,10 +105,60 @@ class FileStorageTests(unittest.TestCase):
f = self.storage.open('storage_test', 'r') f = self.storage.open('storage_test', 'r')
self.assertEqual(f.read(), 'storage contents') self.assertEqual(f.read(), 'storage contents')
f.close() f.close()
self.storage.delete('storage_test') self.storage.delete('storage_test')
self.failIf(self.storage.exists('storage_test')) self.failIf(self.storage.exists('storage_test'))
def test_file_accessed_time(self):
"""
File storage returns a Datetime object for the last accessed time of
a file.
"""
self.failIf(self.storage.exists('test.file'))
f = ContentFile('custom contents')
f_name = self.storage.save('test.file', f)
atime = self.storage.accessed_time(f_name)
self.assertEqual(atime, datetime.fromtimestamp(
os.path.getatime(self.storage.path(f_name))))
self.assertTrue(datetime.now() - self.storage.accessed_time(f_name) < timedelta(seconds=2))
self.storage.delete(f_name)
def test_file_created_time(self):
"""
File storage returns a Datetime object for the creation time of
a file.
"""
self.failIf(self.storage.exists('test.file'))
f = ContentFile('custom contents')
f_name = self.storage.save('test.file', f)
ctime = self.storage.created_time(f_name)
self.assertEqual(ctime, datetime.fromtimestamp(
os.path.getctime(self.storage.path(f_name))))
self.assertTrue(datetime.now() - self.storage.created_time(f_name) < timedelta(seconds=2))
self.storage.delete(f_name)
def test_file_modified_time(self):
"""
File storage returns a Datetime object for the last modified time of
a file.
"""
self.failIf(self.storage.exists('test.file'))
f = ContentFile('custom contents')
f_name = self.storage.save('test.file', f)
mtime = self.storage.modified_time(f_name)
self.assertEqual(mtime, datetime.fromtimestamp(
os.path.getmtime(self.storage.path(f_name))))
self.assertTrue(datetime.now() - self.storage.modified_time(f_name) < timedelta(seconds=2))
self.storage.delete(f_name)
def test_file_save_without_name(self): def test_file_save_without_name(self):
""" """
File storage extracts the filename from the content object if no File storage extracts the filename from the content object if no
@ -215,7 +266,7 @@ class CustomStorage(FileSystemStorage):
class CustomStorageTests(FileStorageTests): class CustomStorageTests(FileStorageTests):
storage_class = CustomStorage storage_class = CustomStorage
def test_custom_get_available_name(self): def test_custom_get_available_name(self):
first = self.storage.save('custom_storage', ContentFile('custom contents')) first = self.storage.save('custom_storage', ContentFile('custom contents'))
self.assertEqual(first, 'custom_storage') self.assertEqual(first, 'custom_storage')