Ticket #12323: 12323.8.diff
File 12323.8.diff, 68.3 KB (added by , 14 years ago) |
---|
-
django/conf/global_settings.py
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 2dabf3b..731c7e1 100644
a b TEMPLATE_CONTEXT_PROCESSORS = ( 194 194 'django.contrib.auth.context_processors.auth', 195 195 'django.core.context_processors.debug', 196 196 'django.core.context_processors.i18n', 197 'django.co re.context_processors.media',197 'django.contrib.staticfiles.context_processors.media', 198 198 # 'django.core.context_processors.request', 199 199 'django.contrib.messages.context_processors.messages', 200 200 ) … … TEMPLATE_CONTEXT_PROCESSORS = ( 202 202 # Output to use in template system for invalid (e.g. misspelled) variables. 203 203 TEMPLATE_STRING_IF_INVALID = '' 204 204 205 # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a206 # trailing slash.207 # Examples: "http://foo.com/media/", "/media/".208 ADMIN_MEDIA_PREFIX = '/media/'209 210 205 # Default e-mail address to use for various automated correspondence from 211 206 # the site managers. 212 207 DEFAULT_FROM_EMAIL = 'webmaster@localhost' … … TEST_DATABASE_COLLATION = None 551 546 552 547 # The list of directories to search for fixtures 553 548 FIXTURE_DIRS = () 549 550 ############### 551 # STATICFILES # 552 ############### 553 554 # Absolute path to the directory that holds media. 555 # Example: "/home/media/media.lawrence.com/static/" 556 STATICFILES_ROOT = '' 557 558 # URL that handles the static files served from STATICFILES_ROOT. 559 # Example: "http://media.lawrence.com/static/" 560 STATICFILES_URL = '/static/' 561 562 # A list of locations of additional static files 563 STATICFILES_DIRS = () 564 565 # The default file storage backend used during the build process 566 STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' 567 568 # List of finder classes that know how to find static files in 569 # various locations. 570 STATICFILES_FINDERS = ( 571 'django.contrib.staticfiles.finders.FileSystemFinder', 572 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 573 # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 574 ) 575 576 # URL prefix for admin media -- CSS, JavaScript and images. 577 # Make sure to use a trailing slash. 578 # Examples: "http://foo.com/static/admin/", "/static/admin/". 579 ADMIN_MEDIA_PREFIX = '/static/admin/' -
django/conf/project_template/settings.py
diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py index 3c783d4..7791b47 100644
a b USE_I18N = True 44 44 USE_L10N = True 45 45 46 46 # Absolute path to the directory that holds media. 47 # Example: "/home/media/media.lawrence.com/ "47 # Example: "/home/media/media.lawrence.com/media/" 48 48 MEDIA_ROOT = '' 49 49 50 50 # URL that handles the media served from MEDIA_ROOT. Make sure to use a … … MEDIA_ROOT = '' 52 52 # Examples: "http://media.lawrence.com", "http://example.com/media/" 53 53 MEDIA_URL = '' 54 54 55 # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 56 # trailing slash. 57 # Examples: "http://foo.com/media/", "/media/". 58 ADMIN_MEDIA_PREFIX = '/media/' 55 # Absolute path to the directory that holds media. 56 # Example: "/home/media/media.lawrence.com/static/" 57 STATICFILES_ROOT = '' 58 59 # URL that handles the static files served from STATICFILES_ROOT. 60 # Example: "http://static.lawrence.com/", "http://example.com/static/" 61 STATICFILES_URL = '/static/' 62 63 # URL prefix for admin media -- CSS, JavaScript and images. 64 # Make sure to use a trailing slash. 65 # Examples: "http://foo.com/static/admin/", "/static/admin/". 66 ADMIN_MEDIA_PREFIX = '/static/admin/' 67 68 # A list of locations of additional static files 69 STATICFILES_DIRS = () 70 71 # List of finder classes that know how to find static files in 72 # various locations. 73 STATICFILES_FINDERS = ( 74 'django.contrib.staticfiles.finders.FileSystemFinder', 75 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 76 # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 77 ) 59 78 60 79 # Make this unique, and don't share it with anybody. 61 80 SECRET_KEY = '' … … INSTALLED_APPS = ( 89 108 'django.contrib.sessions', 90 109 'django.contrib.sites', 91 110 'django.contrib.messages', 111 'django.contrib.staticfiles', 92 112 # Uncomment the next line to enable the admin: 93 113 # 'django.contrib.admin', 94 114 # Uncomment the next line to enable admin documentation: -
new file django/contrib/staticfiles/context_processors.py
diff --git a/django/contrib/staticfiles/__init__.py b/django/contrib/staticfiles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/context_processors.py b/django/contrib/staticfiles/context_processors.py new file mode 100644 index 0000000..e162861
- + 1 from django.conf import settings 2 3 def media(request): 4 return { 5 'STATICFILES_URL': settings.STATICFILES_URL, 6 'MEDIA_URL': settings.MEDIA_URL, 7 } -
new file django/contrib/staticfiles/finders.py
diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py new file mode 100644 index 0000000..eb8c538
- + 1 import os 2 from django.conf import settings 3 from django.db import models 4 from django.core.exceptions import ImproperlyConfigured 5 from django.core.files.storage import default_storage, Storage, FileSystemStorage 6 from django.utils.datastructures import SortedDict 7 from django.utils.functional import memoize, LazyObject 8 from django.utils.importlib import import_module 9 10 from django.contrib.staticfiles import utils 11 from django.contrib.staticfiles.storage import AppMediaStorage 12 13 _finders = {} 14 15 16 class BaseFinder(object): 17 """ 18 A base file finder to be used for custom staticfiles finder classes. 19 20 """ 21 def find(self, path, all=False): 22 """ 23 Given a relative file path this ought to find an 24 absolute file path. 25 26 If the ``all`` parameter is ``False`` (default) only 27 the first found file path will be returned; if set 28 to ``True`` a list of all found files paths is returned. 29 """ 30 raise NotImplementedError() 31 32 def list(self, ignore_patterns=[]): 33 """ 34 Given an optional list of paths to ignore, this should return 35 a three item iterable with path, prefix and a storage instance. 36 """ 37 raise NotImplementedError() 38 39 40 class FileSystemFinder(BaseFinder): 41 """ 42 A static files finder that uses the ``STATICFILES_DIRS`` setting 43 to locate files. 44 """ 45 storages = SortedDict() 46 locations = set() 47 48 def __init__(self, apps=None, *args, **kwargs): 49 for root in settings.STATICFILES_DIRS: 50 if isinstance(root, (list, tuple)): 51 prefix, root = root 52 else: 53 prefix = '' 54 self.locations.add((prefix, root)) 55 # Don't initialize multiple storages for the same location 56 for prefix, root in self.locations: 57 self.storages[root] = FileSystemStorage(location=root) 58 super(FileSystemFinder, self).__init__(*args, **kwargs) 59 60 def find(self, path, all=False): 61 """ 62 Looks for files in the extra media locations 63 as defined in ``STATICFILES_DIRS``. 64 """ 65 matches = [] 66 for prefix, root in self.locations: 67 matched_path = self.find_location(root, path, prefix) 68 if matched_path: 69 if not all: 70 return matched_path 71 matches.append(matched_path) 72 return matches 73 74 def find_location(self, root, path, prefix=None): 75 """ 76 Find a requested static file in a location, returning the found 77 absolute path (or ``None`` if no match). 78 """ 79 if prefix: 80 prefix = '%s/' % prefix 81 if not path.startswith(prefix): 82 return None 83 path = path[len(prefix):] 84 path = os.path.join(root, path) 85 if os.path.exists(path): 86 return path 87 88 def list(self, ignore_patterns): 89 """ 90 List all files in all locations. 91 """ 92 for prefix, root in self.locations: 93 storage = self.storages[root] 94 for path in utils.get_files(storage, ignore_patterns): 95 yield path, prefix, storage 96 97 98 class AppDirectoriesFinder(BaseFinder): 99 """ 100 A static files finder that looks in the ``media`` directory of each app. 101 """ 102 storages = {} 103 storage_class = AppMediaStorage 104 105 def __init__(self, apps=None, *args, **kwargs): 106 if apps is not None: 107 self.apps = apps 108 else: 109 self.apps = models.get_apps() 110 for app in self.apps: 111 self.storages[app] = self.storage_class(app) 112 super(AppDirectoriesFinder, self).__init__(*args, **kwargs) 113 114 def list(self, ignore_patterns): 115 """ 116 List all files in all app storages. 117 """ 118 for storage in self.storages.itervalues(): 119 if storage.is_usable: 120 prefix = storage.get_prefix() 121 for path in utils.get_files(storage, ignore_patterns): 122 yield path, prefix, storage 123 124 def find(self, path, all=False): 125 """ 126 Looks for files in the app directories. 127 """ 128 matches = [] 129 for app in self.apps: 130 app_matches = self.find_in_app(app, path) 131 if app_matches: 132 if not all: 133 return app_matches 134 matches.append(app_matches) 135 return matches 136 137 def find_in_app(self, app, path): 138 """ 139 Find a requested static file in an app's media locations. 140 """ 141 storage = self.storages[app] 142 prefix = storage.get_prefix() 143 if prefix: 144 prefix = '%s/' % prefix 145 if not path.startswith(prefix): 146 return None 147 path = path[len(prefix):] 148 # only try to find a file if the source dir actually exists 149 if storage.is_usable: 150 if storage.exists(path): 151 matched_path = storage.path(path) 152 if matched_path: 153 return matched_path 154 155 156 class DefaultStorageFinder(BaseFinder): 157 """ 158 A static files finder that uses the default storage backend. 159 """ 160 storage = default_storage 161 162 def __init__(self, storage=None, *args, **kwargs): 163 if storage is not None: 164 self.storage = storage 165 # Make sure we have an storage instance here. 166 if not isinstance(self.storage, (Storage, LazyObject)): 167 self.storage = self.storage() 168 super(DefaultStorageFinder, self).__init__(*args, **kwargs) 169 170 def find(self, path, all=False): 171 """ 172 Last resort, looks for files in the default file storage if it's local. 173 """ 174 try: 175 self.storage.path('') 176 except NotImplementedError: 177 pass 178 else: 179 if self.storage.exists(path): 180 match = self.storage.path(path) 181 if all: 182 match = [match] 183 return match 184 return [] 185 186 def list(self, ignore_patterns): 187 """ 188 List all files of the storage. 189 """ 190 for path in utils.get_files(self.storage, ignore_patterns): 191 yield path, '', self.storage 192 193 194 def find(path, all=False): 195 """ 196 Find a requested static file, first looking in any defined extra media 197 locations and next in any (non-excluded) installed apps. 198 199 If no matches are found and the static location is local, look for a match 200 there too. 201 202 If ``all`` is ``False`` (default), return the first matching 203 absolute path (or ``None`` if no match). Otherwise return a list of 204 found absolute paths. 205 206 """ 207 matches = [] 208 for finder in get_finders(): 209 result = finder.find(path, all=all) 210 if not all and result: 211 return result 212 if not isinstance(result, (list, tuple)): 213 result = [result] 214 matches.extend(result) 215 if matches: 216 return matches 217 # No match. 218 return all and [] or None 219 220 def get_finders(): 221 for finder_path in settings.STATICFILES_FINDERS: 222 yield get_finder(finder_path) 223 224 def _get_finder(import_path): 225 """ 226 Imports the message storage class described by import_path, where 227 import_path is the full Python path to the class. 228 """ 229 module, attr = import_path.rsplit('.', 1) 230 try: 231 mod = import_module(module) 232 except ImportError, e: 233 raise ImproperlyConfigured('Error importing module %s: "%s"' % 234 (module, e)) 235 try: 236 Finder = getattr(mod, attr) 237 except AttributeError: 238 raise ImproperlyConfigured('Module "%s" does not define a "%s" ' 239 'class.' % (module, attr)) 240 if not issubclass(Finder, BaseFinder): 241 raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' % 242 (Finder, BaseFinder)) 243 return Finder() 244 get_finder = memoize(_get_finder, _finders, 1) -
new file django/contrib/staticfiles/handlers.py
diff --git a/django/contrib/staticfiles/handlers.py b/django/contrib/staticfiles/handlers.py new file mode 100644 index 0000000..ffd8d1d
- + 1 import os 2 import urllib 3 from urlparse import urlparse 4 5 from django.conf import settings 6 from django.core.handlers.wsgi import WSGIHandler, STATUS_CODE_TEXT 7 from django.http import Http404 8 from django.views import static 9 10 class StaticFilesHandler(WSGIHandler): 11 """ 12 WSGI middleware that intercepts calls to the static files directory, as 13 defined by the STATICFILES_URL setting, and serves those files. 14 """ 15 media_dir = settings.STATICFILES_ROOT 16 media_url = settings.STATICFILES_URL 17 18 def __init__(self, application, media_dir=None): 19 self.application = application 20 if media_dir: 21 self.media_dir = media_dir 22 23 def file_path(self, url): 24 """ 25 Returns the relative path to the media file on disk for the given URL. 26 27 The passed URL is assumed to begin with ``media_url``. If the 28 resultant file path is outside the media directory, then a ValueError 29 is raised. 30 """ 31 # Remove ``media_url``. 32 relative_url = url[len(self.media_url):] 33 return urllib.url2pathname(relative_url) 34 35 def serve(self, request, path): 36 from django.contrib.staticfiles import finders 37 absolute_path = finders.find(path) 38 if not absolute_path: 39 raise Http404('%r could not be matched to a static file.' % path) 40 absolute_path, filename = os.path.split(absolute_path) 41 return static.serve(request, path=filename, document_root=absolute_path) 42 43 def __call__(self, environ, start_response): 44 media_url_bits = urlparse(self.media_url) 45 # Ignore all requests if the host is provided as part of the media_url. 46 # Also ignore requests that aren't under the media path. 47 if (media_url_bits[1] or 48 not environ['PATH_INFO'].startswith(media_url_bits[2])): 49 return self.application(environ, start_response) 50 request = self.application.request_class(environ) 51 try: 52 response = self.serve(request, self.file_path(environ['PATH_INFO'])) 53 except Http404: 54 status = '404 NOT FOUND' 55 start_response(status, {'Content-type': 'text/plain'}.items()) 56 return [str('Page not found: %s' % environ['PATH_INFO'])] 57 status_text = STATUS_CODE_TEXT[response.status_code] 58 status = '%s %s' % (response.status_code, status_text) 59 response_headers = [(str(k), str(v)) for k, v in response.items()] 60 for c in response.cookies.values(): 61 response_headers.append(('Set-Cookie', str(c.output(header='')))) 62 start_response(status, response_headers) 63 return response 64 -
new file django/contrib/staticfiles/management/commands/collectstatic.py
diff --git a/django/contrib/staticfiles/management/__init__.py b/django/contrib/staticfiles/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/management/commands/__init__.py b/django/contrib/staticfiles/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py new file mode 100644 index 0000000..67c7a80
- + 1 import os 2 import sys 3 import shutil 4 from optparse import make_option 5 6 from django.conf import settings 7 from django.core.files.storage import FileSystemStorage, get_storage_class 8 from django.core.management.base import CommandError, NoArgsCommand 9 10 from django.contrib.staticfiles import utils, finders 11 12 class Command(NoArgsCommand): 13 """ 14 Command that allows to copy or symlink media files from different 15 locations to the settings.STATICFILES_ROOT. 16 """ 17 option_list = NoArgsCommand.option_list + ( 18 make_option('--noinput', action='store_false', dest='interactive', 19 default=True, help="Do NOT prompt the user for input of any " 20 "kind."), 21 make_option('-i', '--ignore', action='append', default=[], 22 dest='ignore_patterns', metavar='PATTERN', 23 help="Ignore files or directories matching this glob-style " 24 "pattern. Use multiple times to ignore more."), 25 make_option('-n', '--dry-run', action='store_true', dest='dry_run', 26 help="Do everything except modify the filesystem."), 27 make_option('-l', '--link', action='store_true', dest='link', 28 help="Create a symbolic link to each file instead of copying."), 29 make_option('--no-default-ignore', action='store_false', 30 dest='use_default_ignore_patterns', default=True, 31 help="Don't ignore the common private glob-style patterns 'CVS', " 32 "'.*' and '*~'."), 33 ) 34 help = "Collect static files from apps and other locations in a single location." 35 36 def handle_noargs(self, **options): 37 ignore_patterns = options['ignore_patterns'] 38 if options['use_default_ignore_patterns']: 39 ignore_patterns += ['CVS', '.*', '*~'] 40 ignore_patterns = list(set(ignore_patterns)) 41 self.copied_files = [] 42 self.symlinked_files = [] 43 self.destination_storage = get_storage_class(settings.STATICFILES_STORAGE)() 44 45 try: 46 self.destination_paths = utils.get_files(self.destination_storage, ignore_patterns) 47 except OSError: 48 # The destination storage location may not exist yet. It'll get 49 # created when the first file is copied. 50 self.destination_paths = [] 51 52 try: 53 self.destination_storage.path('') 54 except NotImplementedError: 55 self.destination_local = False 56 else: 57 self.destination_local = True 58 59 if options.get('link', False): 60 if sys.platform == 'win32': 61 raise CommandError("Symlinking is not supported by this " 62 "platform (%s)." % sys.platform) 63 if not self.destination_local: 64 raise CommandError("Can't symlink to a remote destination.") 65 66 # Warn before doing anything more. 67 if options.get('interactive'): 68 confirm = raw_input(""" 69 You have requested to collate static files and collect them at the destination 70 location as specified in your settings file, %r. 71 72 This will overwrite existing files. 73 Are you sure you want to do this? 74 75 Type 'yes' to continue, or 'no' to cancel: """ % settings.STATICFILES_ROOT) 76 if confirm != 'yes': 77 raise CommandError("Static files build cancelled.") 78 79 for finder in finders.get_finders(): 80 for source, prefix, storage in finder.list(ignore_patterns): 81 self.copy_file(source, prefix, storage, **options) 82 83 verbosity = int(options.get('verbosity', 1)) 84 count = len(self.copied_files) + len(self.symlinked_files) 85 if verbosity >= 1: 86 self.stdout.write("%s static file%s collected.\n" % 87 (count, count != 1 and 's' or '')) 88 89 def copy_file(self, source, prefix, source_storage, **options): 90 """ 91 Attempt to copy (or symlink) ``source`` to ``destination``, 92 returning True if successful. 93 """ 94 source_path = source_storage.path(source) 95 if prefix: 96 destination = '/'.join([prefix, source]) 97 else: 98 destination = source 99 dry_run = options.get('dry_run', False) 100 verbosity = int(options.get('verbosity', 1)) 101 102 if destination in self.copied_files: 103 if verbosity >= 2: 104 self.stdout.write("Skipping duplicate file (already copied " 105 "earlier):\n %s\n" % destination) 106 return False 107 if destination in self.symlinked_files: 108 if verbosity >= 2: 109 self.stdout.write("Skipping duplicate file (already linked " 110 "earlier):\n %s\n" % destination) 111 return False 112 if destination in self.destination_paths: 113 if dry_run: 114 if verbosity >= 2: 115 self.stdout.write("Pretending to delete:\n %s\n" 116 % destination) 117 else: 118 if verbosity >= 2: 119 self.stdout.write("Deleting:\n %s\n" % destination) 120 self.destination_storage.delete(destination) 121 122 if options.get('link', False): 123 destination_path = self.destination_storage.path(destination) 124 if dry_run: 125 if verbosity >= 1: 126 self.stdout.write("Pretending to symlink:\n %s\nto:\n %s\n" 127 % (source_path, destination_path)) 128 else: 129 if verbosity >= 1: 130 self.stdout.write("Symlinking:\n %s\nto:\n %s\n" 131 % (source_path, destination_path)) 132 try: 133 os.makedirs(os.path.dirname(destination_path)) 134 except OSError: 135 pass 136 os.symlink(source_path, destination_path) 137 self.symlinked_files.append(destination) 138 else: 139 if dry_run: 140 if verbosity >= 1: 141 self.stdout.write("Pretending to copy:\n %s\nto:\n %s\n" 142 % (source_path, destination)) 143 else: 144 if self.destination_local: 145 destination_path = self.destination_storage.path(destination) 146 try: 147 os.makedirs(os.path.dirname(destination_path)) 148 except OSError: 149 pass 150 shutil.copy2(source_path, destination_path) 151 if verbosity >= 1: 152 self.stdout.write("Copying:\n %s\nto:\n %s\n" 153 % (source_path, destination_path)) 154 else: 155 source_file = source_storage.open(source) 156 self.destination_storage.write(destination, source_file) 157 if verbosity >= 1: 158 self.stdout.write("Copying:\n %s\nto:\n %s\n" 159 % (source_path, destination)) 160 self.copied_files.append(destination) 161 return True -
new file django/contrib/staticfiles/management/commands/findstatic.py
diff --git a/django/contrib/staticfiles/management/commands/findstatic.py b/django/contrib/staticfiles/management/commands/findstatic.py new file mode 100644 index 0000000..0f13277
- + 1 import os 2 from optparse import make_option 3 from django.core.management.base import LabelCommand 4 5 from django.contrib.staticfiles import finders 6 7 class Command(LabelCommand): 8 help = "Finds the absolute paths for the given static file(s)." 9 args = "[file ...]" 10 label = 'static file' 11 option_list = LabelCommand.option_list + ( 12 make_option('--first', action='store_false', dest='all', default=True, 13 help="Only return the first match for each static file."), 14 ) 15 16 def handle_label(self, path, **options): 17 verbosity = int(options.get('verbosity', 1)) 18 result = finders.find(path, all=options['all']) 19 if result: 20 output = '\n '.join((os.path.realpath(path) for path in result)) 21 self.stdout.write("Found %r here:\n %s\n" % (path, output)) 22 else: 23 if verbosity >= 1: 24 self.stdout.write("No matching file found for %r.\n" % path) -
new file django/contrib/staticfiles/storage.py
diff --git a/django/contrib/staticfiles/models.py b/django/contrib/staticfiles/models.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py new file mode 100644 index 0000000..07609ad
- + 1 import os 2 from django.conf import settings 3 from django.core.exceptions import ImproperlyConfigured 4 from django.core.files.storage import FileSystemStorage 5 from django.utils.importlib import import_module 6 7 from django.contrib.staticfiles import utils 8 9 10 class StaticFilesStorage(FileSystemStorage): 11 """ 12 Standard file system storage for site media files. 13 14 The defaults for ``location`` and ``base_url`` are 15 ``STATICFILES_ROOT`` and ``STATICFILES_URL``. 16 """ 17 def __init__(self, location=None, base_url=None, *args, **kwargs): 18 if location is None: 19 location = settings.STATICFILES_ROOT 20 if base_url is None: 21 base_url = settings.STATICFILES_URL 22 if not location: 23 raise ImproperlyConfigured("You're using the staticfiles app " 24 "without having set the STATICFILES_ROOT setting. Set it to " 25 "the absolute path of the directory that holds static media.") 26 if not base_url: 27 raise ImproperlyConfigured("You're using the staticfiles app " 28 "without having set the STATICFILES_URL setting. Set it to " 29 "URL that handles the files served from STATICFILES_ROOT.") 30 super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs) 31 32 33 class AppMediaStorage(FileSystemStorage): 34 """ 35 A file system storage backend that takes an app module and works 36 for the ``media`` directory of it. 37 """ 38 source_dir = 'media' 39 40 def __init__(self, app, *args, **kwargs): 41 """ 42 Returns a static file storage if available in the given app. 43 44 """ 45 # ``app`` is actually the models module of the app. 46 # Remove the '.models'. 47 bits = app.__name__.split('.')[:-1] 48 self.app = app 49 self.app_name = bits[-1] 50 self.app_module = '.'.join(bits) 51 52 # The models module (``app``) may be a package in which case 53 # ``dirname(app.__file__)`` would be wrong. 54 # Import the actual app as opposed to the models module. 55 app = import_module(self.app_module) 56 app_root = os.path.dirname(app.__file__) 57 self.location = os.path.join(app_root, self.source_dir) 58 super(AppMediaStorage, self).__init__(self.location, *args, **kwargs) 59 60 @property 61 def is_usable(self): 62 return os.path.isdir(self.location) 63 64 def get_prefix(self): 65 """ 66 Return the path name that should be prepended to files for this app. 67 """ 68 if self.app_module == 'django.contrib.admin': 69 return self.app_name 70 return None 71 72 def get_files(self, ignore_patterns=[]): 73 """ 74 Return a list containing the relative source paths for all files that 75 should be copied for an app. 76 """ 77 files = [] 78 prefix = self.get_prefix() 79 for path in utils.get_files(self, ignore_patterns): 80 if prefix: 81 path = '/'.join([prefix, path]) 82 files.append(path) 83 return files -
new file django/contrib/staticfiles/urls.py
diff --git a/django/contrib/staticfiles/urls.py b/django/contrib/staticfiles/urls.py new file mode 100644 index 0000000..131b102
- + 1 import re 2 from django.conf import settings 3 from django.conf.urls.defaults import patterns, url, include 4 from django.core.exceptions import ImproperlyConfigured 5 6 urlpatterns = [] 7 8 # only serve non-fqdn URLs 9 if not settings.DEBUG: 10 urlpatterns += patterns('', 11 url(r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve'), 12 ) 13 14 def staticfiles_urlpatterns(prefix=None): 15 """ 16 Helper function to return a URL pattern for serving static files. 17 """ 18 if settings.DEBUG: 19 return [] 20 if prefix is None: 21 prefix = settings.STATICFILES_URL 22 if '://' in prefix: 23 raise ImproperlyConfigured( 24 "The STATICFILES_URL setting is a full URL, not a path and " 25 "can't be used with the urls.staticfiles_urlpatterns() helper.") 26 if prefix.startswith("/"): 27 prefix = prefix[1:] 28 return patterns('', 29 url(r'^%s' % re.escape(prefix), include(urlpatterns)),) -
new file django/contrib/staticfiles/utils.py
diff --git a/django/contrib/staticfiles/utils.py b/django/contrib/staticfiles/utils.py new file mode 100644 index 0000000..11d5269
- + 1 import os 2 import fnmatch 3 4 def get_files(storage, ignore_patterns=[], location=''): 5 """ 6 Recursively walk the storage directories gathering a complete list of files 7 that should be copied, returning this list. 8 9 """ 10 def is_ignored(path): 11 """ 12 Return True or False depending on whether the ``path`` should be 13 ignored (if it matches any pattern in ``ignore_patterns``). 14 15 """ 16 for pattern in ignore_patterns: 17 if fnmatch.fnmatchcase(path, pattern): 18 return True 19 return False 20 21 directories, files = storage.listdir(location) 22 static_files = [location and '/'.join([location, fn]) or fn 23 for fn in files 24 if not is_ignored(fn)] 25 for dir in directories: 26 if is_ignored(dir): 27 continue 28 if location: 29 dir = '/'.join([location, dir]) 30 static_files.extend(get_files(storage, ignore_patterns, dir)) 31 return static_files -
new file django/contrib/staticfiles/views.py
diff --git a/django/contrib/staticfiles/views.py b/django/contrib/staticfiles/views.py new file mode 100644 index 0000000..ac53a6f
- + 1 """ 2 Views and functions for serving static files. These are only to be used during 3 development, and SHOULD NOT be used in a production setting. 4 5 """ 6 import os 7 from django import http 8 from django.conf import settings 9 from django.core.exceptions import ImproperlyConfigured 10 from django.views import static 11 12 from django.contrib.staticfiles import finders 13 14 15 def serve(request, path, show_indexes=False): 16 """ 17 Serve static files from locations inferred from the static files finders. 18 19 To use, put a URL pattern such as:: 20 21 (r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve') 22 23 in your URLconf. You may also set ``show_indexes`` to ``True`` if you'd 24 like to serve a basic index of the directory. This index view will use the 25 template hardcoded below, but if you'd like to override it, you can create 26 a template called ``static/directory_index``. 27 """ 28 if settings.DEBUG: 29 raise ImproperlyConfigured("The view to serve static files can only " 30 "be used if the DEBUG setting is True") 31 absolute_path = finders.find(path) 32 if not absolute_path: 33 raise http.Http404("%r could not be matched to a static file." % path) 34 absolute_path, filename = os.path.split(absolute_path) 35 return static.serve(request, path=filename, document_root=absolute_path, 36 show_indexes=show_indexes) -
django/core/context_processors.py
diff --git a/django/core/context_processors.py b/django/core/context_processors.py index 7a59728..f24c6cf 100644
a b def media(request): 71 71 Adds media-related context variables to the context. 72 72 73 73 """ 74 return {'MEDIA_URL': settings.MEDIA_URL} 74 import warnings 75 warnings.warn( 76 "The context processor at `django.core.context_processors.media` is " \ 77 "deprecated; use the path `django.contrib.staticfiles.context_processors.media` " \ 78 "instead.", 79 PendingDeprecationWarning 80 ) 81 from django.contrib.staticfiles.context_processors import media as media_context_processor 82 return media_context_processor(request) 75 83 76 84 def request(request): 77 85 return {'request': request} -
django/core/management/base.py
diff --git a/django/core/management/base.py b/django/core/management/base.py index 6b9ce6e..e8b72f0 100644
a b class BaseCommand(object): 199 199 stderr. 200 200 201 201 """ 202 verbosity = options.get('verbosity', 1) 202 203 # Switch to English, because django-admin.py creates database content 203 204 # like permissions, and those shouldn't contain any translations. 204 205 # But only do this if we can assume we have a working settings file, -
django/core/management/commands/runserver.py
diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index fc2c694..21391e8 100644
a b 1 from django.core.management.base import BaseCommand, CommandError2 1 from optparse import make_option 3 2 import os 4 3 import sys 4 import warnings 5 6 from django.core.management.base import BaseCommand, CommandError 5 7 6 8 class Command(BaseCommand): 7 9 option_list = BaseCommand.option_list + ( … … class Command(BaseCommand): 20 22 import django 21 23 from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException 22 24 from django.core.handlers.wsgi import WSGIHandler 25 from django.contrib.staticfiles.handlers import StaticFilesHandler 23 26 if args: 24 27 raise CommandError('Usage is runserver %s' % self.args) 25 28 if not addrport: … … class Command(BaseCommand): 56 59 translation.activate(settings.LANGUAGE_CODE) 57 60 58 61 try: 59 handler = AdminMediaHandler(WSGIHandler(), admin_media_path) 62 handler = WSGIHandler() 63 handler = StaticFilesHandler(handler) 64 # serve admin media like old-school (deprecation pending) 65 handler = AdminMediaHandler(handler, admin_media_path) 60 66 run(addr, int(port), handler) 61 67 except WSGIServerException, e: 62 68 # Use helpful error messages instead of ugly tracebacks. -
django/core/servers/basehttp.py
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index dae4297..2da05de 100644
a b been reviewed for security issues. Don't use it for production use. 8 8 """ 9 9 10 10 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 11 import mimetypes12 11 import os 13 12 import re 14 import stat15 13 import sys 16 14 import urllib 15 import warnings 17 16 18 17 from django.core.management.color import color_style 19 18 from django.utils.http import http_date 20 19 from django.utils._os import safe_join 20 from django.contrib.staticfiles.handlers import StaticFilesHandler 21 from django.views import static 21 22 22 23 __version__ = "0.1" 23 24 __all__ = ['WSGIServer','WSGIRequestHandler'] … … class WSGIRequestHandler(BaseHTTPRequestHandler): 633 634 634 635 sys.stderr.write(msg) 635 636 636 class AdminMediaHandler(object): 637 638 class AdminMediaHandler(StaticFilesHandler): 637 639 """ 638 640 WSGI middleware that intercepts calls to the admin media directory, as 639 641 defined by the ADMIN_MEDIA_PREFIX setting, and serves those images. 640 642 Use this ONLY LOCALLY, for development! This hasn't been tested for 641 643 security and is not super efficient. 642 644 """ 643 def __init__(self, application, media_dir=None): 645 646 @property 647 def media_dir(self): 648 import django 649 return os.path.join(django.__path__[0], 'contrib', 'admin', 'media') 650 651 @property 652 def media_url(self): 644 653 from django.conf import settings 645 self.application = application 646 if not media_dir: 647 import django 648 self.media_dir = \ 649 os.path.join(django.__path__[0], 'contrib', 'admin', 'media') 650 else: 651 self.media_dir = media_dir 652 self.media_url = settings.ADMIN_MEDIA_PREFIX 654 return settings.ADMIN_MEDIA_PREFIX 655 656 def __init__(self, application, media_dir=None): 657 warnings.warn('The AdminMediaHandler handler is deprecated; use the ' 658 '`django.contrib.staticfiles.handlers.StaticFilesHandler` instead.', 659 PendingDeprecationWarning) 660 super(AdminMediaHandler, self).__init__(application, media_dir) 653 661 654 662 def file_path(self, url): 655 663 """ 656 664 Returns the path to the media file on disk for the given URL. 657 665 658 The passed URL is assumed to begin with ADMIN_MEDIA_PREFIX. If the666 The passed URL is assumed to begin with ``media_url``. If the 659 667 resultant file path is outside the media directory, then a ValueError 660 668 is raised. 661 669 """ 662 # Remove ADMIN_MEDIA_PREFIX.670 # Remove ``media_url``. 663 671 relative_url = url[len(self.media_url):] 664 672 relative_path = urllib.url2pathname(relative_url) 665 673 return safe_join(self.media_dir, relative_path) 666 674 667 def __call__(self, environ, start_response): 668 import os.path 669 670 # Ignore requests that aren't under ADMIN_MEDIA_PREFIX. Also ignore 671 # all requests if ADMIN_MEDIA_PREFIX isn't a relative URL. 672 if self.media_url.startswith('http://') or self.media_url.startswith('https://') \ 673 or not environ['PATH_INFO'].startswith(self.media_url): 674 return self.application(environ, start_response) 675 def serve(self, request, path): 676 document_root, path = os.path.split(path) 677 return static.serve(request, path, document_root=document_root) 675 678 676 # Find the admin file and serve it up, if it exists and is readable.677 try:678 file_path = self.file_path(environ['PATH_INFO'])679 except ValueError: # Resulting file path was not valid.680 status = '404 NOT FOUND'681 headers = {'Content-type': 'text/plain'}682 output = ['Page not found: %s' % environ['PATH_INFO']]683 start_response(status, headers.items())684 return output685 if not os.path.exists(file_path):686 status = '404 NOT FOUND'687 headers = {'Content-type': 'text/plain'}688 output = ['Page not found: %s' % environ['PATH_INFO']]689 else:690 try:691 fp = open(file_path, 'rb')692 except IOError:693 status = '401 UNAUTHORIZED'694 headers = {'Content-type': 'text/plain'}695 output = ['Permission denied: %s' % environ['PATH_INFO']]696 else:697 # This is a very simple implementation of conditional GET with698 # the Last-Modified header. It makes media files a bit speedier699 # because the files are only read off disk for the first700 # request (assuming the browser/client supports conditional701 # GET).702 mtime = http_date(os.stat(file_path)[stat.ST_MTIME])703 headers = {'Last-Modified': mtime}704 if environ.get('HTTP_IF_MODIFIED_SINCE', None) == mtime:705 status = '304 NOT MODIFIED'706 output = []707 else:708 status = '200 OK'709 mime_type = mimetypes.guess_type(file_path)[0]710 if mime_type:711 headers['Content-Type'] = mime_type712 output = [fp.read()]713 fp.close()714 start_response(status, headers.items())715 return output716 679 717 680 def run(addr, port, wsgi_handler): 718 681 server_address = (addr, port) -
docs/index.txt
diff --git a/docs/index.txt b/docs/index.txt index e456d04..61ce622 100644
a b Other batteries included 185 185 * :doc:`Signals <topics/signals>` 186 186 * :doc:`Sitemaps <ref/contrib/sitemaps>` 187 187 * :doc:`Sites <ref/contrib/sites>` 188 * :doc:`Static Files <ref/contrib/staticfiles>` 188 189 * :doc:`Syndication feeds (RSS/Atom) <ref/contrib/syndication>` 189 190 * :doc:`Unicode in Django <ref/unicode>` 190 191 * :doc:`Web design helpers <ref/contrib/webdesign>` -
docs/ref/contrib/index.txt
diff --git a/docs/ref/contrib/index.txt b/docs/ref/contrib/index.txt index 90edf72..5e308dc 100644
a b those packages have. 38 38 redirects 39 39 sitemaps 40 40 sites 41 staticfiles 41 42 syndication 42 43 webdesign 43 44 -
new file docs/ref/contrib/staticfiles.txt
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt new file mode 100644 index 0000000..a88a807
- + 1 =================== 2 The staticfiles app 3 =================== 4 5 .. module:: django.contrib.staticfiles 6 :synopsis: An app for handling static files. 7 8 .. versionadded:: 1.3 9 10 This is a Django app that provides helpers for serving static files. 11 12 Helpers 13 ======= 14 15 ``media`` context processor 16 --------------------------- 17 18 To refer to static file assets from a template, ensure you have set the 19 :ref:`staticfiles-url` setting to the URL path where the static files are 20 served. 21 22 Next, add the ``media`` context processor to your 23 :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting:: 24 25 TEMPLATE_CONTEXT_PROCESSORS = ( 26 'django.contrib.staticfiles.context_processors.media', 27 ) 28 29 Templates rendered with ``RequestContext`` will now have access to a 30 :ref:`staticfiles-url` context variable, e.g.: 31 32 .. code-block:: html+django 33 34 <link href="{{ STATICFILES_URL }}css/polls.css" rel="stylesheet" type="text/css" /> 35 36 Serving static files during development 37 --------------------------------------- 38 39 .. warning:: Don't use this on production servers. 40 This feature is **only intended for development**. 41 Please, don't shoot yourself in the foot. Thanks. 42 43 To serve static files in and :ref:`staticfiles-url` add the following snippet 44 to the end of your primary URL configuration:: 45 46 from django.conf import settings 47 48 if settings.DEBUG: 49 urlpatterns = patterns('django.contrib.staticfiles.views', 50 url(r'^site_media/static/(?P<path>.*)$', 'serve'), 51 ) 52 53 Alternatively, you can make use of the helper function 54 :function:`django.contrib.staticfiles.urls.staticfiles_urlpatterns` to append 55 the proper URL pattern for serving static files to your already defined 56 pattern list:: 57 58 from django.contrib.staticfiles.urls import staticfiles_urlpatterns 59 60 # ... 61 62 urlpatterns += staticfiles_urlpatterns() 63 64 Note, the begin of the pattern ``r'^static/'`` should be equal to your 65 ``STATICFILES_URL`` setting. 66 67 Management Commands 68 =================== 69 70 .. highlight:: console 71 72 .. _collectstatic: 73 74 collectstatic 75 ------------- 76 77 Collects the static files from all activated finders in the 78 :ref:`staticfiles-storage`:: 79 80 $ python manage.py collectstatic 81 82 Duplicate file names are resolved in a similar way to how template resolution 83 works. Files are searched by using the 84 :ref:`enabled finders <staticfiles-finders>`. The default is to look in all 85 locations defined in :ref:`staticfiles-dirs` and in the ``media`` directory 86 of apps specified by the :setting:`INSTALLED_APPS` setting. 87 88 Some commonly used options are: 89 90 - ``--noinput`` 91 Do NOT prompt the user for input of any kind. 92 93 - ``-i PATTERN`` or ``--ignore=PATTERN`` 94 Ignore files or directories matching this glob-style pattern. Use multiple 95 times to ignore more. 96 97 - ``-n`` or ``--dry-run`` 98 Do everything except modify the filesystem. 99 100 - ``-l`` or ``--link`` 101 Create a symbolic link to each file instead of copying. 102 103 - ``--no-default-ignore`` 104 Don't ignore the common private glob-style patterns ``'CVS'``, ``'.*'`` 105 and ``'*~'``. 106 107 For a full list of options, refer to the collectstatic management command 108 help by running:: 109 110 $ python manage.py collectstatic --help 111 112 .. _findstatic: 113 114 findstatic 115 ---------- 116 117 Searches for one or more relative paths with the enabled finders. 118 For example:: 119 120 $ python manage.py findstatic css/base.css admin/js/core.js 121 /home/special.polls.com/core/media/css/base.css 122 /home/polls.com/core/media/css/base.css 123 /home/polls.com/src/django/contrib/admin/media/js/core.js 124 125 By default, all matching locations are found. To only return the first match 126 for each relative path, use the ``--first`` option:: 127 128 $ python manage.py findstatic css/base.css --first 129 /home/special.polls.com/core/media/css/base.css 130 131 Settings 132 ======== 133 134 .. highlight:: python 135 136 STATICFILES_ROOT 137 ---------------- 138 139 Default: ``''`` (Empty string) 140 141 The absolute path to the directory that holds static files:: 142 143 STATICFILES_ROOT = "/home/polls.com/polls/site_media/static/" 144 145 This is used by the default static files storage backend (i.e. if you use a 146 different ``STATICFILES_STORAGE``, you don't need to set this). 147 148 .. _staticfiles-url: 149 150 STATICFILES_URL 151 --------------- 152 153 Default: ``'/static/'`` 154 155 URL that handles the files served from STATICFILES_ROOT, e.g.:: 156 157 STATICFILES_URL = '/site_media/static/' 158 159 Note that this should **always** have a trailing slash. 160 161 .. _staticfiles-dirs: 162 163 STATICFILES_DIRS 164 ---------------- 165 166 Default: ``[]`` 167 168 This setting defines the additional locations the staticfiles app will 169 traverse if the ``FileSystemFinder`` finder is enabled, e.g. if you use the 170 :ref:`collectstatic` or :ref:`findstatic` management command or use the 171 static file serving view. 172 173 It should be defined as a sequence of ``(prefix, path)`` tuples, e.g.:: 174 175 STATICFILES_DIRS = ( 176 ('', '/home/special.polls.com/polls/media'), 177 ('', '/home/polls.com/polls/media'), 178 ('common', '/opt/webfiles/common'), 179 ) 180 181 .. _staticfiles-storage: 182 183 STATICFILES_STORAGE 184 ------------------- 185 186 Default: ``'django.contrib.staticfiles.storage.StaticFilesStorage'`` 187 188 The storage to use when collecting static files to a single location with 189 the :ref:`collectstatic` management command. 190 191 .. _staticfiles-finders: 192 193 STATICFILES_FINDERS 194 ------------------- 195 196 Default:: 197 198 ("django.contrib.staticfiles.finders.FileSystemFinder", 199 "django.contrib.staticfiles.finders.AppDirectoriesFinder") 200 201 The list of finder backends that know how to find static files in 202 various locations. 203 204 If you know you only keep your files in one of those locations, just omit 205 the unnecessary finders. 206 207 One finder is disabled by default: 208 :class:`django.contrib.staticfiles.finders.DefaultStorageFinder`. If added to 209 your :ref:`staticfiles-finders` setting, it will look for static files in the 210 default file storage as defined by the :setting:`DEFAULT_FILE_STORAGE` 211 setting. 212 213 .. note:: When using the ``AppDirectoriesFinder`` finder, make sure 214 your apps can be found by Django's app loading mechanism. Simply include 215 a ``models`` module (an empty ``models.py`` file suffices) and add the 216 app to the :setting:`INSTALLED_APPS` setting of your site. -
docs/ref/settings.txt
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 6ad5af9..d4e6e53 100644
a b Default:: 1482 1482 ("django.contrib.auth.context_processors.auth", 1483 1483 "django.core.context_processors.debug", 1484 1484 "django.core.context_processors.i18n", 1485 "django.co re.context_processors.media",1485 "django.contrib.staticfiles.context_processors.media", 1486 1486 "django.contrib.messages.context_processors.messages") 1487 1487 1488 1488 A tuple of callables that are used to populate the context in ``RequestContext``. -
docs/ref/templates/api.txt
diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 2ac4e65..e2614f2 100644
a b and return a dictionary of items to be merged into the context. By default, 309 309 ("django.contrib.auth.context_processors.auth", 310 310 "django.core.context_processors.debug", 311 311 "django.core.context_processors.i18n", 312 "django.co re.context_processors.media",312 "django.contrib.staticfiles.context_processors.media", 313 313 "django.contrib.messages.context_processors.messages") 314 314 315 315 .. versionadded:: 1.2 … … If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every 432 432 ``RequestContext`` will contain a variable ``MEDIA_URL``, providing the 433 433 value of the :setting:`MEDIA_URL` setting. 434 434 435 .. versionchanged:: 1.3 436 This context processor has been moved to the new :ref:`staticfiles` app. 437 Please use the new ``django.contrib.staticfiles.context_processors.media`` 438 context processor. 439 435 440 django.core.context_processors.csrf 436 441 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 437 442 -
tests/regressiontests/servers/tests.py
diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py index 4763982..29f169c 100644
a b from django.test import TestCase 9 9 from django.core.handlers.wsgi import WSGIHandler 10 10 from django.core.servers.basehttp import AdminMediaHandler 11 11 12 from django.conf import settings 12 13 13 14 class AdminMediaHandlerTests(TestCase): 14 15 … … class AdminMediaHandlerTests(TestCase): 25 26 """ 26 27 # Cases that should work on all platforms. 27 28 data = ( 28 (' /media/css/base.css', ('css', 'base.css')),29 ('%scss/base.css' % settings.ADMIN_MEDIA_PREFIX, ('css', 'base.css')), 29 30 ) 30 31 # Cases that should raise an exception. 31 32 bad_data = () … … class AdminMediaHandlerTests(TestCase): 34 35 if os.sep == '/': 35 36 data += ( 36 37 # URL, tuple of relative path parts. 37 (' /media/\\css/base.css', ('\\css', 'base.css')),38 ('%s\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, ('\\css', 'base.css')), 38 39 ) 39 40 bad_data += ( 40 ' /media//css/base.css',41 ' /media////css/base.css',42 ' /media/../css/base.css',41 '%s/css/base.css' % settings.ADMIN_MEDIA_PREFIX, 42 '%s///css/base.css' % settings.ADMIN_MEDIA_PREFIX, 43 '%s../css/base.css' % settings.ADMIN_MEDIA_PREFIX, 43 44 ) 44 45 elif os.sep == '\\': 45 46 bad_data += ( 46 ' /media/C:\css/base.css',47 ' /media//\\css/base.css',48 ' /media/\\css/base.css',49 ' /media/\\\\css/base.css'47 '%sC:\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 48 '%s/\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 49 '%s\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 50 '%s\\\\css/base.css' % settings.ADMIN_MEDIA_PREFIX 50 51 ) 51 52 for url, path_tuple in data: 52 53 try: -
new file tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt
diff --git a/tests/regressiontests/staticfiles_tests/__init__.py b/tests/regressiontests/staticfiles_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/__init__.py b/tests/regressiontests/staticfiles_tests/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/__init__.py b/tests/regressiontests/staticfiles_tests/apps/no_label/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt b/tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt new file mode 100644 index 0000000..aa264ca
- + 1 file2 in no_label_app -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden
diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/models.py b/tests/regressiontests/staticfiles_tests/apps/no_label/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/__init__.py b/tests/regressiontests/staticfiles_tests/apps/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden b/tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS b/tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~ b/tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~ new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt b/tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt new file mode 100644 index 0000000..169a206
- + 1 In app media directory. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt b/tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt new file mode 100644 index 0000000..9f9a8d9
- + 1 file1 in the app dir 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme b/tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme new file mode 100644 index 0000000..d7df09c
- + 1 This file should be ignored. 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/models.py b/tests/regressiontests/staticfiles_tests/apps/test/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt b/tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt new file mode 100644 index 0000000..c62c93d
- + 1 File in otherdir. -
new file tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt
diff --git a/tests/regressiontests/staticfiles_tests/models.py b/tests/regressiontests/staticfiles_tests/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt b/tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt new file mode 100644 index 0000000..04326a2
- + 1 Can we find this file? -
new file tests/regressiontests/staticfiles_tests/project/documents/test.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/documents/test.txt b/tests/regressiontests/staticfiles_tests/project/documents/test.txt new file mode 100644 index 0000000..04326a2
- + 1 Can we find this file? -
new file tests/regressiontests/staticfiles_tests/project/documents/test/file.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/documents/test/file.txt b/tests/regressiontests/staticfiles_tests/project/documents/test/file.txt new file mode 100644 index 0000000..fdeaa23
- + 1 In STATICFILES_DIRS directory. 2 -
new file tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt b/tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt new file mode 100644 index 0000000..466922d
- + 1 Media file. -
new file tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt b/tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt new file mode 100644 index 0000000..2eda9ce
- + 1 Yeah! 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/tests.py
diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py new file mode 100644 index 0000000..bf38839
- + 1 import tempfile 2 import shutil 3 import os 4 import sys 5 from cStringIO import StringIO 6 import posixpath 7 8 from django.test import TestCase, Client 9 from django.conf import settings 10 from django.core.exceptions import ImproperlyConfigured 11 from django.core.management import call_command 12 from django.db.models.loading import load_app 13 14 from django.contrib.staticfiles import finders, storage 15 16 TEST_ROOT = os.path.dirname(__file__) 17 18 19 class StaticFilesTestCase(TestCase): 20 """ 21 Test case with a couple utility assertions. 22 """ 23 def setUp(self): 24 self.old_staticfiles_url = settings.STATICFILES_URL 25 self.old_staticfiles_root = settings.STATICFILES_ROOT 26 self.old_staticfiles_dirs = settings.STATICFILES_DIRS 27 self.old_staticfiles_finders = settings.STATICFILES_FINDERS 28 self.old_installed_apps = settings.INSTALLED_APPS 29 self.old_media_root = settings.MEDIA_ROOT 30 self.old_media_url = settings.MEDIA_URL 31 self.old_admin_media_prefix = settings.ADMIN_MEDIA_PREFIX 32 33 # We have to load these apps to test staticfiles. 34 load_app('regressiontests.staticfiles_tests.apps.test') 35 load_app('regressiontests.staticfiles_tests.apps.no_label') 36 site_media = os.path.join(TEST_ROOT, 'project', 'site_media') 37 settings.MEDIA_ROOT = os.path.join(site_media, 'media') 38 settings.MEDIA_URL = '/media/' 39 settings.ADMIN_MEDIA_PREFIX = posixpath.join(settings.STATICFILES_URL, 'admin/') 40 settings.STATICFILES_ROOT = os.path.join(site_media, 'static') 41 settings.STATICFILES_URL = '/static/' 42 settings.STATICFILES_DIRS = ( 43 os.path.join(TEST_ROOT, 'project', 'documents'), 44 ) 45 settings.STATICFILES_FINDERS = ( 46 'django.contrib.staticfiles.finders.FileSystemFinder', 47 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 48 'django.contrib.staticfiles.finders.DefaultStorageFinder', 49 ) 50 51 def tearDown(self): 52 settings.MEDIA_ROOT = self.old_media_root 53 settings.MEDIA_URL = self.old_media_url 54 settings.ADMIN_MEDIA_PREFIX = self.old_admin_media_prefix 55 settings.STATICFILES_ROOT = self.old_staticfiles_root 56 settings.STATICFILES_URL = self.old_staticfiles_url 57 settings.STATICFILES_DIRS = self.old_staticfiles_dirs 58 settings.STATICFILES_FINDERS = self.old_staticfiles_finders 59 settings.INSTALLED_APPS = self.old_installed_apps 60 61 def assertFileContains(self, filepath, text): 62 self.failUnless(text in self._get_file(filepath), 63 "'%s' not in '%s'" % (text, filepath)) 64 65 def assertFileNotFound(self, filepath): 66 self.assertRaises(IOError, self._get_file, filepath) 67 68 69 class BuildStaticTestCase(StaticFilesTestCase): 70 """ 71 Tests shared by all file-resolving features (collectstatic, 72 findstatic, and static serve view). 73 74 This relies on the asserts defined in UtilityAssertsTestCase, but 75 is separated because some test cases need those asserts without 76 all these tests. 77 """ 78 def setUp(self): 79 super(BuildStaticTestCase, self).setUp() 80 self.old_staticfiles_storage = settings.STATICFILES_STORAGE 81 self.old_root = settings.STATICFILES_ROOT 82 settings.STATICFILES_ROOT = tempfile.mkdtemp() 83 self.run_collectstatic() 84 85 def tearDown(self): 86 shutil.rmtree(settings.STATICFILES_ROOT) 87 settings.STATICFILES_ROOT = self.old_root 88 super(BuildStaticTestCase, self).tearDown() 89 90 def run_collectstatic(self, **kwargs): 91 call_command('collectstatic', interactive=False, verbosity='0', 92 ignore_patterns=['*.ignoreme'], **kwargs) 93 94 def _get_file(self, filepath): 95 assert filepath, 'filepath is empty.' 96 filepath = os.path.join(settings.STATICFILES_ROOT, filepath) 97 return open(filepath).read() 98 99 100 class TestDefaults(object): 101 """ 102 A few standard test cases. 103 """ 104 def test_staticfiles_dirs(self): 105 """ 106 Can find a file in a STATICFILES_DIRS directory. 107 108 """ 109 self.assertFileContains('test.txt', 'Can we find') 110 111 def test_staticfiles_dirs_subdir(self): 112 """ 113 Can find a file in a subdirectory of a STATICFILES_DIRS 114 directory. 115 116 """ 117 self.assertFileContains('subdir/test.txt', 'Can we find') 118 119 def test_staticfiles_dirs_priority(self): 120 """ 121 File in STATICFILES_DIRS has priority over file in app. 122 123 """ 124 self.assertFileContains('test/file.txt', 'STATICFILES_DIRS') 125 126 def test_app_files(self): 127 """ 128 Can find a file in an app media/ directory. 129 130 """ 131 self.assertFileContains('test/file1.txt', 'file1 in the app dir') 132 133 134 class TestBuildStatic(BuildStaticTestCase, TestDefaults): 135 """ 136 Test ``collectstatic`` management command. 137 """ 138 def test_ignore(self): 139 """ 140 Test that -i patterns are ignored. 141 """ 142 self.assertFileNotFound('test/test.ignoreme') 143 144 def test_common_ignore_patterns(self): 145 """ 146 Common ignore patterns (*~, .*, CVS) are ignored. 147 """ 148 self.assertFileNotFound('test/.hidden') 149 self.assertFileNotFound('test/backup~') 150 self.assertFileNotFound('test/CVS') 151 152 153 class TestBuildStaticExcludeNoDefaultIgnore(BuildStaticTestCase, TestDefaults): 154 """ 155 Test ``--exclude-dirs`` and ``--no-default-ignore`` options for 156 ``collectstatic`` management command. 157 """ 158 def run_collectstatic(self): 159 super(TestBuildStaticExcludeNoDefaultIgnore, self).run_collectstatic( 160 use_default_ignore_patterns=False) 161 162 def test_no_common_ignore_patterns(self): 163 """ 164 With --no-default-ignore, common ignore patterns (*~, .*, CVS) 165 are not ignored. 166 167 """ 168 self.assertFileContains('test/.hidden', 'should be ignored') 169 self.assertFileContains('test/backup~', 'should be ignored') 170 self.assertFileContains('test/CVS', 'should be ignored') 171 172 173 class TestBuildStaticDryRun(BuildStaticTestCase): 174 """ 175 Test ``--dry-run`` option for ``collectstatic`` management command. 176 """ 177 def run_collectstatic(self): 178 super(TestBuildStaticDryRun, self).run_collectstatic(dry_run=True) 179 180 def test_no_files_created(self): 181 """ 182 With --dry-run, no files created in destination dir. 183 """ 184 self.assertEquals(os.listdir(settings.STATICFILES_ROOT), []) 185 186 187 if sys.platform != 'win32': 188 class TestBuildStaticLinks(BuildStaticTestCase, TestDefaults): 189 """ 190 Test ``--link`` option for ``collectstatic`` management command. 191 192 Note that by inheriting ``TestDefaults`` we repeat all 193 the standard file resolving tests here, to make sure using 194 ``--link`` does not change the file-selection semantics. 195 """ 196 def run_collectstatic(self): 197 super(TestBuildStaticLinks, self).run_collectstatic(link=True) 198 199 def test_links_created(self): 200 """ 201 With ``--link``, symbolic links are created. 202 203 """ 204 self.failUnless(os.path.islink(os.path.join(settings.STATICFILES_ROOT, 'test.txt'))) 205 206 207 class TestServeStatic(StaticFilesTestCase): 208 """ 209 Test static asset serving view. 210 """ 211 urls = "regressiontests.staticfiles_tests.urls" 212 213 def _response(self, filepath): 214 return self.client.get( 215 posixpath.join(settings.STATICFILES_URL, filepath)) 216 217 def assertFileContains(self, filepath, text): 218 self.assertContains(self._response(filepath), text) 219 220 def assertFileNotFound(self, filepath): 221 self.assertEquals(self._response(filepath).status_code, 404) 222 223 224 class TestServeAdminMedia(TestServeStatic): 225 """ 226 Test serving media from django.contrib.admin. 227 """ 228 def test_serve_admin_media(self): 229 media_file = posixpath.join( 230 settings.ADMIN_MEDIA_PREFIX, 'css/base.css') 231 response = self.client.get(media_file) 232 self.assertContains(response, 'body') 233 234 235 class FinderTestCase(object): 236 """ 237 Base finder test mixin 238 """ 239 def test_find_first(self): 240 src, dst = self.find_first 241 self.assertEquals(self.finder.find(src), dst) 242 243 def test_find_all(self): 244 src, dst = self.find_all 245 self.assertEquals(self.finder.find(src, all=True), dst) 246 247 248 class TestFileSystemFinder(StaticFilesTestCase, FinderTestCase): 249 """ 250 Test FileSystemFinder. 251 """ 252 def setUp(self): 253 super(TestFileSystemFinder, self).setUp() 254 self.finder = finders.FileSystemFinder() 255 test_file_path = os.path.join(TEST_ROOT, 'project/documents/test/file.txt') 256 self.find_first = ("test/file.txt", test_file_path) 257 self.find_all = ("test/file.txt", [test_file_path]) 258 259 260 class TestAppDirectoriesFinder(StaticFilesTestCase, FinderTestCase): 261 """ 262 Test AppDirectoriesFinder. 263 """ 264 def setUp(self): 265 super(TestAppDirectoriesFinder, self).setUp() 266 self.finder = finders.AppDirectoriesFinder() 267 test_file_path = os.path.join(TEST_ROOT, 'apps/test/media/test/file1.txt') 268 self.find_first = ("test/file1.txt", test_file_path) 269 self.find_all = ("test/file1.txt", [test_file_path]) 270 271 272 class TestDefaultStorageFinder(StaticFilesTestCase, FinderTestCase): 273 """ 274 Test DefaultStorageFinder. 275 """ 276 def setUp(self): 277 super(TestDefaultStorageFinder, self).setUp() 278 self.finder = finders.DefaultStorageFinder( 279 storage=storage.StaticFilesStorage(location=settings.MEDIA_ROOT)) 280 test_file_path = os.path.join(settings.MEDIA_ROOT, 'media-file.txt') 281 self.find_first = ("media-file.txt", test_file_path) 282 self.find_all = ("media-file.txt", [test_file_path]) 283 284 285 class TestMiscFinder(TestCase): 286 """ 287 A few misc finder tests. 288 """ 289 def test_get_finder(self): 290 self.assertTrue(isinstance(finders.get_finder( 291 "django.contrib.staticfiles.finders.FileSystemFinder"), 292 finders.FileSystemFinder)) 293 self.assertRaises(ImproperlyConfigured, 294 finders.get_finder, "django.contrib.staticfiles.finders.FooBarFinder") 295 self.assertRaises(ImproperlyConfigured, 296 finders.get_finder, "foo.bar.FooBarFinder") 297 -
new file tests/regressiontests/staticfiles_tests/urls.py
diff --git a/tests/regressiontests/staticfiles_tests/urls.py b/tests/regressiontests/staticfiles_tests/urls.py new file mode 100644 index 0000000..061ec64
- + 1 from django.conf import settings 2 from django.conf.urls.defaults import * 3 4 urlpatterns = patterns('', 5 url(r'^static/(?P<path>.*)$', 'django.contrib.staticfiles.views.serve'), 6 ) -
tests/runtests.py
diff --git a/tests/runtests.py b/tests/runtests.py index a5f7479..055c910 100755
a b ALWAYS_INSTALLED_APPS = [ 27 27 'django.contrib.comments', 28 28 'django.contrib.admin', 29 29 'django.contrib.admindocs', 30 'django.contrib.staticfiles', 30 31 ] 31 32 32 33 def get_test_models(): -
tests/urls.py
diff --git a/tests/urls.py b/tests/urls.py index 01d6408..e06dc33 100644
a b urlpatterns = patterns('', 41 41 42 42 # special headers views 43 43 (r'special_headers/', include('regressiontests.special_headers.urls')), 44 45 # static files handling 46 (r'^', include('regressiontests.staticfiles_tests.urls')), 44 47 )