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:
parent
4d4d68a2cd
commit
58b704d8da
|
@ -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
|
||||||
|
|
|
@ -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)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue