1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

newforms-admin: Merged from trunk up to [7877].

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7881 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Brian Rosner 2008-07-10 20:47:18 +00:00
parent 9cbd07b681
commit 60c060c476
38 changed files with 1341 additions and 306 deletions

View File

@ -287,6 +287,7 @@ answer newbie questions, and generally made Django that much better:
Neal Norwitz <nnorwitz@google.com> Neal Norwitz <nnorwitz@google.com>
Todd O'Bryan <toddobryan@mac.com> Todd O'Bryan <toddobryan@mac.com>
oggie rob <oz.robharvey@gmail.com> oggie rob <oz.robharvey@gmail.com>
oggy <ognjen.maric@gmail.com>
Jay Parlar <parlar@gmail.com> Jay Parlar <parlar@gmail.com>
Carlos Eduardo de Paula <carlosedp@gmail.com> Carlos Eduardo de Paula <carlosedp@gmail.com>
pavithran s <pavithran.s@gmail.com> pavithran s <pavithran.s@gmail.com>

View File

@ -1,6 +1,6 @@
from django.contrib.admin.filterspecs import FilterSpec from django.contrib.admin.filterspecs import FilterSpec
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.core.paginator import QuerySetPaginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.db import models from django.db import models
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils.encoding import force_unicode, smart_str from django.utils.encoding import force_unicode, smart_str
@ -109,8 +109,7 @@ class ChangeList(object):
return '?%s' % urlencode(p) return '?%s' % urlencode(p)
def get_results(self, request): def get_results(self, request):
paginator = QuerySetPaginator(self.query_set, self.list_per_page) paginator = Paginator(self.query_set, self.list_per_page)
# Get the number of objects, with admin filters applied. # Get the number of objects, with admin filters applied.
try: try:
result_count = paginator.count result_count = paginator.count

View File

@ -3,12 +3,40 @@ Classes representing uploaded files.
""" """
import os import os
import tempfile
import warnings
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
from StringIO import StringIO from StringIO import StringIO
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile') from django.conf import settings
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile', 'SimpleUploadedFile')
# Because we fooled around with it a bunch, UploadedFile has a bunch
# of deprecated properties. This little shortcut helps define 'em
# without too much code duplication.
def deprecated_property(old, new, readonly=False):
def issue_warning():
warnings.warn(
message = "UploadedFile.%s is deprecated; use UploadedFile.%s instead." % (old, new),
category = DeprecationWarning,
stacklevel = 3
)
def getter(self):
issue_warning()
return getattr(self, new)
def setter(self, value):
issue_warning()
setattr(self, new, value)
if readonly:
return property(getter)
else:
return property(getter, setter)
class UploadedFile(object): class UploadedFile(object):
""" """
@ -20,16 +48,19 @@ class UploadedFile(object):
""" """
DEFAULT_CHUNK_SIZE = 64 * 2**10 DEFAULT_CHUNK_SIZE = 64 * 2**10
def __init__(self, file_name=None, content_type=None, file_size=None, charset=None): def __init__(self, name=None, content_type=None, size=None, charset=None):
self.file_name = file_name self.name = name
self.file_size = file_size self.size = size
self.content_type = content_type self.content_type = content_type
self.charset = charset self.charset = charset
def __repr__(self): def __repr__(self):
return "<%s: %s (%s)>" % (self.__class__.__name__, self.file_name, self.content_type) return "<%s: %s (%s)>" % (self.__class__.__name__, self.name, self.content_type)
def _set_file_name(self, name): def _get_name(self):
return self._name
def _set_name(self, name):
# Sanitize the file name so that it can't be dangerous. # Sanitize the file name so that it can't be dangerous.
if name is not None: if name is not None:
# Just use the basename of the file -- anything else is dangerous. # Just use the basename of the file -- anything else is dangerous.
@ -40,14 +71,11 @@ class UploadedFile(object):
name, ext = os.path.splitext(name) name, ext = os.path.splitext(name)
name = name[:255 - len(ext)] + ext name = name[:255 - len(ext)] + ext
self._file_name = name self._name = name
def _get_file_name(self): name = property(_get_name, _set_name)
return self._file_name
file_name = property(_get_file_name, _set_file_name) def chunks(self, chunk_size=None):
def chunk(self, chunk_size=None):
""" """
Read the file and yield chucks of ``chunk_size`` bytes (defaults to Read the file and yield chucks of ``chunk_size`` bytes (defaults to
``UploadedFile.DEFAULT_CHUNK_SIZE``). ``UploadedFile.DEFAULT_CHUNK_SIZE``).
@ -58,12 +86,27 @@ class UploadedFile(object):
if hasattr(self, 'seek'): if hasattr(self, 'seek'):
self.seek(0) self.seek(0)
# Assume the pointer is at zero... # Assume the pointer is at zero...
counter = self.file_size counter = self.size
while counter > 0: while counter > 0:
yield self.read(chunk_size) yield self.read(chunk_size)
counter -= chunk_size counter -= chunk_size
# Deprecated properties
filename = deprecated_property(old="filename", new="name")
file_name = deprecated_property(old="file_name", new="name")
file_size = deprecated_property(old="file_size", new="size")
chunk = deprecated_property(old="chunk", new="chunks", readonly=True)
def _get_data(self):
warnings.warn(
message = "UploadedFile.data is deprecated; use UploadedFile.read() instead.",
category = DeprecationWarning,
stacklevel = 2
)
return self.read()
data = property(_get_data)
def multiple_chunks(self, chunk_size=None): def multiple_chunks(self, chunk_size=None):
""" """
Returns ``True`` if you can expect multiple chunks. Returns ``True`` if you can expect multiple chunks.
@ -74,9 +117,9 @@ class UploadedFile(object):
""" """
if not chunk_size: if not chunk_size:
chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE
return self.file_size < chunk_size return self.size > chunk_size
# Abstract methods; subclasses *must* default read() and probably should # Abstract methods; subclasses *must* define read() and probably should
# define open/close. # define open/close.
def read(self, num_bytes=None): def read(self, num_bytes=None):
raise NotImplementedError() raise NotImplementedError()
@ -87,23 +130,49 @@ class UploadedFile(object):
def close(self): def close(self):
pass pass
def xreadlines(self):
return self
def readlines(self):
return list(self.xreadlines())
def __iter__(self):
# Iterate over this file-like object by newlines
buffer_ = None
for chunk in self.chunks():
chunk_buffer = StringIO(chunk)
for line in chunk_buffer:
if buffer_:
line = buffer_ + line
buffer_ = None
# If this is the end of a line, yield
# otherwise, wait for the next round
if line[-1] in ('\n', '\r'):
yield line
else:
buffer_ = line
if buffer_ is not None:
yield buffer_
# Backwards-compatible support for uploaded-files-as-dictionaries. # Backwards-compatible support for uploaded-files-as-dictionaries.
def __getitem__(self, key): def __getitem__(self, key):
import warnings
warnings.warn( warnings.warn(
message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.", message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.",
category = DeprecationWarning, category = DeprecationWarning,
stacklevel = 2 stacklevel = 2
) )
backwards_translate = { backwards_translate = {
'filename': 'file_name', 'filename': 'name',
'content-type': 'content_type', 'content-type': 'content_type',
} }
if key == 'content': if key == 'content':
return self.read() return self.read()
elif key == 'filename': elif key == 'filename':
return self.file_name return self.name
elif key == 'content-type': elif key == 'content-type':
return self.content_type return self.content_type
else: else:
@ -113,34 +182,36 @@ class TemporaryUploadedFile(UploadedFile):
""" """
A file uploaded to a temporary location (i.e. stream-to-disk). A file uploaded to a temporary location (i.e. stream-to-disk).
""" """
def __init__(self, name, content_type, size, charset):
def __init__(self, file, file_name, content_type, file_size, charset): super(TemporaryUploadedFile, self).__init__(name, content_type, size, charset)
super(TemporaryUploadedFile, self).__init__(file_name, content_type, file_size, charset) if settings.FILE_UPLOAD_TEMP_DIR:
self.file = file self._file = tempfile.NamedTemporaryFile(suffix='.upload', dir=settings.FILE_UPLOAD_TEMP_DIR)
self.path = file.name else:
self.file.seek(0) self._file = tempfile.NamedTemporaryFile(suffix='.upload')
def temporary_file_path(self): def temporary_file_path(self):
""" """
Returns the full path of this file. Returns the full path of this file.
""" """
return self.path return self.name
def read(self, *args, **kwargs): # Most methods on this object get proxied to NamedTemporaryFile.
return self.file.read(*args, **kwargs) # We can't directly subclass because NamedTemporaryFile is actually a
# factory function
def open(self): def read(self, *args): return self._file.read(*args)
self.seek(0) def seek(self, offset): return self._file.seek(offset)
def write(self, s): return self._file.write(s)
def seek(self, *args, **kwargs): def close(self): return self._file.close()
self.file.seek(*args, **kwargs) def __iter__(self): return iter(self._file)
def readlines(self, size=None): return self._file.readlines(size)
def xreadlines(self): return self._file.xreadlines()
class InMemoryUploadedFile(UploadedFile): class InMemoryUploadedFile(UploadedFile):
""" """
A file uploaded into memory (i.e. stream-to-memory). A file uploaded into memory (i.e. stream-to-memory).
""" """
def __init__(self, file, field_name, file_name, content_type, file_size, charset): def __init__(self, file, field_name, name, content_type, size, charset):
super(InMemoryUploadedFile, self).__init__(file_name, content_type, file_size, charset) super(InMemoryUploadedFile, self).__init__(name, content_type, size, charset)
self.file = file self.file = file
self.field_name = field_name self.field_name = field_name
self.file.seek(0) self.file.seek(0)
@ -154,7 +225,7 @@ class InMemoryUploadedFile(UploadedFile):
def read(self, *args, **kwargs): def read(self, *args, **kwargs):
return self.file.read(*args, **kwargs) return self.file.read(*args, **kwargs)
def chunk(self, chunk_size=None): def chunks(self, chunk_size=None):
self.file.seek(0) self.file.seek(0)
yield self.read() yield self.read()
@ -168,9 +239,9 @@ class SimpleUploadedFile(InMemoryUploadedFile):
""" """
def __init__(self, name, content, content_type='text/plain'): def __init__(self, name, content, content_type='text/plain'):
self.file = StringIO(content or '') self.file = StringIO(content or '')
self.file_name = name self.name = name
self.field_name = None self.field_name = None
self.file_size = len(content or '') self.size = len(content or '')
self.content_type = content_type self.content_type = content_type
self.charset = None self.charset = None
self.file.seek(0) self.file.seek(0)

View File

@ -132,21 +132,15 @@ class TemporaryFileUploadHandler(FileUploadHandler):
Create the file object to append to as data is coming in. Create the file object to append to as data is coming in.
""" """
super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs) super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs)
self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR) self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset)
self.write = self.file.write
def receive_data_chunk(self, raw_data, start): def receive_data_chunk(self, raw_data, start):
self.write(raw_data) self.file.write(raw_data)
def file_complete(self, file_size): def file_complete(self, file_size):
self.file.seek(0) self.file.seek(0)
return TemporaryUploadedFile( self.file.size = file_size
file = self.file, return self.file
file_name = self.file_name,
content_type = self.content_type,
file_size = file_size,
charset = self.charset
)
class MemoryFileUploadHandler(FileUploadHandler): class MemoryFileUploadHandler(FileUploadHandler):
""" """
@ -189,37 +183,12 @@ class MemoryFileUploadHandler(FileUploadHandler):
return InMemoryUploadedFile( return InMemoryUploadedFile(
file = self.file, file = self.file,
field_name = self.field_name, field_name = self.field_name,
file_name = self.file_name, name = self.file_name,
content_type = self.content_type, content_type = self.content_type,
file_size = file_size, size = file_size,
charset = self.charset charset = self.charset
) )
class TemporaryFile(object):
"""
A temporary file that tries to delete itself when garbage collected.
"""
def __init__(self, dir):
if not dir:
dir = tempfile.gettempdir()
try:
(fd, name) = tempfile.mkstemp(suffix='.upload', dir=dir)
self.file = os.fdopen(fd, 'w+b')
except (OSError, IOError):
raise OSError("Could not create temporary file for uploading, have you set settings.FILE_UPLOAD_TEMP_DIR correctly?")
self.name = name
def __getattr__(self, name):
a = getattr(self.__dict__['file'], name)
if type(a) != type(0):
setattr(self, name, a)
return a
def __del__(self):
try:
os.unlink(self.name)
except OSError:
pass
def load_handler(path, *args, **kwargs): def load_handler(path, *args, **kwargs):
""" """

View File

@ -6,8 +6,12 @@ from django.dispatch import dispatcher
class BaseHandler(object): class BaseHandler(object):
# Changes that are always applied to a response (in this order). # Changes that are always applied to a response (in this order).
response_fixes = [http.fix_location_header, response_fixes = [
http.conditional_content_removal] http.fix_location_header,
http.conditional_content_removal,
http.fix_IE_for_attach,
http.fix_IE_for_vary,
]
def __init__(self): def __init__(self):
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None

View File

@ -205,10 +205,12 @@ class EmailMessage(object):
conversions. conversions.
""" """
if to: if to:
assert not isinstance(to, basestring), '"to" argument must be a list or tuple'
self.to = list(to) self.to = list(to)
else: else:
self.to = [] self.to = []
if bcc: if bcc:
assert not isinstance(bcc, basestring), '"bcc" argument must be a list or tuple'
self.bcc = list(bcc) self.bcc = list(bcc)
else: else:
self.bcc = [] self.bcc = []

View File

@ -1,6 +1,12 @@
class InvalidPage(Exception): class InvalidPage(Exception):
pass pass
class PageNotAnInteger(InvalidPage):
pass
class EmptyPage(InvalidPage):
pass
class Paginator(object): class Paginator(object):
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
self.object_list = object_list self.object_list = object_list
@ -14,14 +20,14 @@ class Paginator(object):
try: try:
number = int(number) number = int(number)
except ValueError: except ValueError:
raise InvalidPage('That page number is not an integer') raise PageNotAnInteger('That page number is not an integer')
if number < 1: if number < 1:
raise InvalidPage('That page number is less than 1') raise EmptyPage('That page number is less than 1')
if number > self.num_pages: if number > self.num_pages:
if number == 1 and self.allow_empty_first_page: if number == 1 and self.allow_empty_first_page:
pass pass
else: else:
raise InvalidPage('That page contains no results') raise EmptyPage('That page contains no results')
return number return number
def page(self, number): def page(self, number):
@ -36,7 +42,11 @@ class Paginator(object):
def _get_count(self): def _get_count(self):
"Returns the total number of objects, across all pages." "Returns the total number of objects, across all pages."
if self._count is None: if self._count is None:
self._count = len(self.object_list) from django.db.models.query import QuerySet
if isinstance(self.object_list, QuerySet):
self._count = self.object_list.count()
else:
self._count = len(self.object_list)
return self._count return self._count
count = property(_get_count) count = property(_get_count)
@ -61,15 +71,7 @@ class Paginator(object):
return range(1, self.num_pages + 1) return range(1, self.num_pages + 1)
page_range = property(_get_page_range) page_range = property(_get_page_range)
class QuerySetPaginator(Paginator): QuerySetPaginator = Paginator # For backwards-compatibility.
"""
Like Paginator, but works on QuerySets.
"""
def _get_count(self):
if self._count is None:
self._count = self.object_list.count()
return self._count
count = property(_get_count)
class Page(object): class Page(object):
def __init__(self, object_list, number, paginator): def __init__(self, object_list, number, paginator):
@ -133,14 +135,14 @@ class ObjectPaginator(Paginator):
try: try:
page_number = int(page_number) + 1 page_number = int(page_number) + 1
except ValueError: except ValueError:
raise InvalidPage raise PageNotAnInteger
return self.validate_number(page_number) return self.validate_number(page_number)
def get_page(self, page_number): def get_page(self, page_number):
try: try:
page_number = int(page_number) + 1 page_number = int(page_number) + 1
except ValueError: except ValueError:
raise InvalidPage raise PageNotAnInteger
return self.page(page_number).object_list return self.page(page_number).object_list
def has_next_page(self, page_number): def has_next_page(self, page_number):

View File

@ -3,6 +3,10 @@ import types
import sys import sys
import os import os
from itertools import izip from itertools import izip
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback.
import django.db.models.manipulators # Imported to register signal handler. import django.db.models.manipulators # Imported to register signal handler.
import django.db.models.manager # Ditto. import django.db.models.manager # Ditto.
@ -23,13 +27,11 @@ from django.core.files.move import file_move_safe
from django.core.files import locks from django.core.files import locks
from django.conf import settings from django.conf import settings
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
class ModelBase(type): class ModelBase(type):
"Metaclass for all models" """
Metaclass for all models.
"""
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
super_new = super(ModelBase, cls).__new__ super_new = super(ModelBase, cls).__new__
parents = [b for b in bases if isinstance(b, ModelBase)] parents = [b for b in bases if isinstance(b, ModelBase)]
@ -141,7 +143,9 @@ class ModelBase(type):
setattr(cls, name, value) setattr(cls, name, value)
def _prepare(cls): def _prepare(cls):
# Creates some methods once self._meta has been populated. """
Creates some methods once self._meta has been populated.
"""
opts = cls._meta opts = cls._meta
opts._prepare(cls) opts._prepare(cls)
@ -160,6 +164,7 @@ class ModelBase(type):
dispatcher.send(signal=signals.class_prepared, sender=cls) dispatcher.send(signal=signals.class_prepared, sender=cls)
class Model(object): class Model(object):
__metaclass__ = ModelBase __metaclass__ = ModelBase
@ -264,7 +269,7 @@ class Model(object):
def save(self): def save(self):
""" """
Save the current instance. Override this in a subclass if you want to Saves the current instance. Override this in a subclass if you want to
control the saving process. control the saving process.
""" """
self.save_base() self.save_base()
@ -368,10 +373,12 @@ class Model(object):
def _collect_sub_objects(self, seen_objs, parent=None, nullable=False): def _collect_sub_objects(self, seen_objs, parent=None, nullable=False):
""" """
Recursively populates seen_objs with all objects related to this object. Recursively populates seen_objs with all objects related to this
object.
When done, seen_objs.items() will be in the format: When done, seen_objs.items() will be in the format:
[(model_class, {pk_val: obj, pk_val: obj, ...}), [(model_class, {pk_val: obj, pk_val: obj, ...}),
(model_class, {pk_val: obj, pk_val: obj, ...}),...] (model_class, {pk_val: obj, pk_val: obj, ...}), ...]
""" """
pk_val = self._get_pk_val() pk_val = self._get_pk_val()
if seen_objs.add(self.__class__, pk_val, self, parent, nullable): if seen_objs.add(self.__class__, pk_val, self, parent, nullable):
@ -408,11 +415,11 @@ class Model(object):
def delete(self): def delete(self):
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname) assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
# Find all the objects than need to be deleted # Find all the objects than need to be deleted.
seen_objs = CollectedObjects() seen_objs = CollectedObjects()
self._collect_sub_objects(seen_objs) self._collect_sub_objects(seen_objs)
# Actually delete the objects # Actually delete the objects.
delete_objects(seen_objs) delete_objects(seen_objs)
delete.alters_data = True delete.alters_data = True
@ -451,12 +458,12 @@ class Model(object):
return getattr(self, cachename) return getattr(self, cachename)
def _get_FIELD_filename(self, field): def _get_FIELD_filename(self, field):
if getattr(self, field.attname): # value is not blank if getattr(self, field.attname): # Value is not blank.
return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))) return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)))
return '' return ''
def _get_FIELD_url(self, field): def _get_FIELD_url(self, field):
if getattr(self, field.attname): # value is not blank if getattr(self, field.attname): # Value is not blank.
import urlparse import urlparse
return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
return '' return ''
@ -471,16 +478,14 @@ class Model(object):
except OSError: # Directory probably already exists. except OSError: # Directory probably already exists.
pass pass
#
# Check for old-style usage (files-as-dictionaries). Warn here first # Check for old-style usage (files-as-dictionaries). Warn here first
# since there are multiple locations where we need to support both new # since there are multiple locations where we need to support both new
# and old usage. # and old usage.
#
if isinstance(raw_field, dict): if isinstance(raw_field, dict):
import warnings import warnings
warnings.warn( warnings.warn(
message = "Representing uploaded files as dictionaries is"\ message = "Representing uploaded files as dictionaries is"\
" deprected. Use django.core.files.SimpleUploadedFile"\ " deprecated. Use django.core.files.SimpleUploadedFile"\
" instead.", " instead.",
category = DeprecationWarning, category = DeprecationWarning,
stacklevel = 2 stacklevel = 2
@ -505,41 +510,35 @@ class Model(object):
filename = field.get_filename(filename) filename = field.get_filename(filename)
# # If the filename already exists, keep adding an underscore to the name
# If the filename already exists, keep adding an underscore to the name of # of the file until the filename doesn't exist.
# the file until the filename doesn't exist.
#
while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)): while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
try: try:
dot_index = filename.rindex('.') dot_index = filename.rindex('.')
except ValueError: # filename has no dot except ValueError: # filename has no dot.
filename += '_' filename += '_'
else: else:
filename = filename[:dot_index] + '_' + filename[dot_index:] filename = filename[:dot_index] + '_' + filename[dot_index:]
#
# Save the file name on the object and write the file to disk
#
# Save the file name on the object and write the file to disk.
setattr(self, field.attname, filename) setattr(self, field.attname, filename)
full_filename = self._get_FIELD_filename(field) full_filename = self._get_FIELD_filename(field)
if hasattr(raw_field, 'temporary_file_path'): if hasattr(raw_field, 'temporary_file_path'):
# This file has a file path that we can move. # This file has a file path that we can move.
raw_field.close() raw_field.close()
file_move_safe(raw_field.temporary_file_path(), full_filename) file_move_safe(raw_field.temporary_file_path(), full_filename)
else: else:
# This is a normal uploadedfile that we can stream. # This is a normal uploadedfile that we can stream.
fp = open(full_filename, 'wb') fp = open(full_filename, 'wb')
locks.lock(fp, locks.LOCK_EX) locks.lock(fp, locks.LOCK_EX)
for chunk in raw_field.chunk(): for chunk in raw_field.chunks():
fp.write(chunk) fp.write(chunk)
locks.unlock(fp) locks.unlock(fp)
fp.close() fp.close()
# Save the width and/or height, if applicable. # Save the width and/or height, if applicable.
if isinstance(field, ImageField) and (field.width_field or field.height_field): if isinstance(field, ImageField) and \
(field.width_field or field.height_field):
from django.utils.images import get_image_dimensions from django.utils.images import get_image_dimensions
width, height = get_image_dimensions(full_filename) width, height = get_image_dimensions(full_filename)
if field.width_field: if field.width_field:
@ -547,7 +546,7 @@ class Model(object):
if field.height_field: if field.height_field:
setattr(self, field.height_field, height) setattr(self, field.height_field, height)
# Save the object because it has changed unless save is False # Save the object because it has changed, unless save is False.
if save: if save:
self.save() self.save()
@ -567,6 +566,7 @@ class Model(object):
setattr(self, cachename, get_image_dimensions(filename)) setattr(self, cachename, get_image_dimensions(filename))
return getattr(self, cachename) return getattr(self, cachename)
############################################ ############################################
# HELPER FUNCTIONS (CURRIED MODEL METHODS) # # HELPER FUNCTIONS (CURRIED MODEL METHODS) #
############################################ ############################################
@ -582,6 +582,7 @@ def method_set_order(ordered_obj, self, id_list):
ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i) ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
transaction.commit_unless_managed() transaction.commit_unless_managed()
def method_get_order(ordered_obj, self): def method_get_order(ordered_obj, self):
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name) rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
order_name = ordered_obj._meta.order_with_respect_to.name order_name = ordered_obj._meta.order_with_respect_to.name
@ -589,6 +590,7 @@ def method_get_order(ordered_obj, self):
return [r[pk_name] for r in return [r[pk_name] for r in
ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)] ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)]
############################################## ##############################################
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) # # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
############################################## ##############################################
@ -596,6 +598,7 @@ def method_get_order(ordered_obj, self):
def get_absolute_url(opts, func, self, *args, **kwargs): def get_absolute_url(opts, func, self, *args, **kwargs):
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs) return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)
######## ########
# MISC # # MISC #
######## ########
@ -607,8 +610,6 @@ if sys.version_info < (2, 5):
# Prior to Python 2.5, Exception was an old-style class # Prior to Python 2.5, Exception was an old-style class
def subclass_exception(name, parent, unused): def subclass_exception(name, parent, unused):
return types.ClassType(name, (parent,), {}) return types.ClassType(name, (parent,), {})
else: else:
def subclass_exception(name, parent, module): def subclass_exception(name, parent, module):
return type(name, (parent,), {'__module__': module}) return type(name, (parent,), {'__module__': module})

View File

@ -751,9 +751,12 @@ class FileField(Field):
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
"Returns field's value prepared for saving into a database." "Returns field's value prepared for saving into a database."
# Need to convert UploadedFile objects provided via a form to unicode for database insertion # Need to convert UploadedFile objects provided via a form to unicode for database insertion
if value is None: if hasattr(value, 'name'):
return value.name
elif value is None:
return None return None
return unicode(value) else:
return unicode(value)
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
@ -827,7 +830,7 @@ class FileField(Field):
# We don't need to raise a warning because Model._save_FIELD_file will # We don't need to raise a warning because Model._save_FIELD_file will
# do so for us. # do so for us.
try: try:
file_name = file.file_name file_name = file.name
except AttributeError: except AttributeError:
file_name = file['filename'] file_name = file['filename']
@ -842,9 +845,9 @@ class FileField(Field):
return os.path.normpath(f) return os.path.normpath(f)
def save_form_data(self, instance, data): def save_form_data(self, instance, data):
from django.newforms.fields import UploadedFile from django.core.files.uploadedfile import UploadedFile
if data and isinstance(data, UploadedFile): if data and isinstance(data, UploadedFile):
getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False) getattr(instance, "save_%s_file" % self.name)(data.name, data, save=False)
def formfield(self, **kwargs): def formfield(self, **kwargs):
defaults = {'form_class': forms.FileField} defaults = {'form_class': forms.FileField}

View File

@ -13,19 +13,20 @@ from django.utils.datastructures import SortedDict
CHUNK_SIZE = 100 CHUNK_SIZE = 100
ITER_CHUNK_SIZE = CHUNK_SIZE ITER_CHUNK_SIZE = CHUNK_SIZE
# Pull into this namespace for backwards compatibility # Pull into this namespace for backwards compatibility.
EmptyResultSet = sql.EmptyResultSet EmptyResultSet = sql.EmptyResultSet
class CyclicDependency(Exception): class CyclicDependency(Exception):
pass pass
class CollectedObjects(object): class CollectedObjects(object):
""" """
A container that stores keys and lists of values along with A container that stores keys and lists of values along with remembering the
remembering the parent objects for all the keys. parent objects for all the keys.
This is used for the database object deletion routines so that we This is used for the database object deletion routines so that we can
can calculate the 'leaf' objects which should be deleted first. calculate the 'leaf' objects which should be deleted first.
""" """
def __init__(self): def __init__(self):
@ -34,26 +35,27 @@ class CollectedObjects(object):
def add(self, model, pk, obj, parent_model, nullable=False): def add(self, model, pk, obj, parent_model, nullable=False):
""" """
Adds an item. Adds an item to the container.
model is the class of the object being added,
pk is the primary key, obj is the object itself,
parent_model is the model of the parent object
that this object was reached through, nullable should
be True if this relation is nullable.
If the item already existed in the structure, Arguments:
returns true, otherwise false. * model - the class of the object being added.
* pk - the primary key.
* obj - the object itself.
* parent_model - the model of the parent object that this object was
reached through.
* nullable - should be True if this relation is nullable.
Returns True if the item already existed in the structure and
False otherwise.
""" """
d = self.data.setdefault(model, SortedDict()) d = self.data.setdefault(model, SortedDict())
retval = pk in d retval = pk in d
d[pk] = obj d[pk] = obj
# Nullable relationships can be ignored -- they # Nullable relationships can be ignored -- they are nulled out before
# are nulled out before deleting, and therefore # deleting, and therefore do not affect the order in which objects
# do not affect the order in which objects have # have to be deleted.
# to be deleted.
if parent_model is not None and not nullable: if parent_model is not None and not nullable:
self.children.setdefault(parent_model, []).append(model) self.children.setdefault(parent_model, []).append(model)
return retval return retval
def __contains__(self, key): def __contains__(self, key):
@ -77,8 +79,8 @@ class CollectedObjects(object):
def ordered_keys(self): def ordered_keys(self):
""" """
Returns the models in the order that they should be Returns the models in the order that they should be dealt with (i.e.
dealth with i.e. models with no dependencies first. models with no dependencies first).
""" """
dealt_with = SortedDict() dealt_with = SortedDict()
# Start with items that have no children # Start with items that have no children
@ -91,19 +93,22 @@ class CollectedObjects(object):
dealt_with[model] = None dealt_with[model] = None
found = True found = True
if not found: if not found:
raise CyclicDependency("There is a cyclic dependency of items to be processed.") raise CyclicDependency(
"There is a cyclic dependency of items to be processed.")
return dealt_with.keys() return dealt_with.keys()
def unordered_keys(self): def unordered_keys(self):
""" """
Fallback for the case where is a cyclic dependency but we Fallback for the case where is a cyclic dependency but we don't care.
don't care.
""" """
return self.data.keys() return self.data.keys()
class QuerySet(object): class QuerySet(object):
"Represents a lazy database lookup for a set of objects" """
Represents a lazy database lookup for a set of objects.
"""
def __init__(self, model=None, query=None): def __init__(self, model=None, query=None):
self.model = model self.model = model
self.query = query or sql.Query(self.model, connection) self.query = query or sql.Query(self.model, connection)
@ -116,7 +121,7 @@ class QuerySet(object):
def __getstate__(self): def __getstate__(self):
""" """
Allows the Queryset to be pickled. Allows the QuerySet to be pickled.
""" """
# Force the cache to be fully populated. # Force the cache to be fully populated.
len(self) len(self)
@ -131,7 +136,7 @@ class QuerySet(object):
def __len__(self): def __len__(self):
# Since __len__ is called quite frequently (for example, as part of # Since __len__ is called quite frequently (for example, as part of
# list(qs), we make some effort here to be as efficient as possible # list(qs), we make some effort here to be as efficient as possible
# whilst not messing up any existing iterators against the queryset. # whilst not messing up any existing iterators against the QuerySet.
if self._result_cache is None: if self._result_cache is None:
if self._iter: if self._iter:
self._result_cache = list(self._iter) self._result_cache = list(self._iter)
@ -173,7 +178,9 @@ class QuerySet(object):
return True return True
def __getitem__(self, k): def __getitem__(self, k):
"Retrieve an item or slice from the set of results." """
Retrieves an item or slice from the set of results.
"""
if not isinstance(k, (slice, int, long)): if not isinstance(k, (slice, int, long)):
raise TypeError raise TypeError
assert ((not isinstance(k, slice) and (k >= 0)) assert ((not isinstance(k, slice) and (k >= 0))
@ -264,7 +271,7 @@ class QuerySet(object):
Performs a SELECT COUNT() and returns the number of records as an Performs a SELECT COUNT() and returns the number of records as an
integer. integer.
If the queryset is already cached (i.e. self._result_cache is set) this If the QuerySet is already cached (i.e. self._result_cache is set) this
simply returns the length of the cached results set to avoid multiple simply returns the length of the cached results set to avoid multiple
SELECT COUNT(*) calls. SELECT COUNT(*) calls.
""" """
@ -290,7 +297,7 @@ class QuerySet(object):
def create(self, **kwargs): def create(self, **kwargs):
""" """
Create a new object with the given kwargs, saving it to the database Creates a new object with the given kwargs, saving it to the database
and returning the created object. and returning the created object.
""" """
obj = self.model(**kwargs) obj = self.model(**kwargs)
@ -425,8 +432,8 @@ class QuerySet(object):
def dates(self, field_name, kind, order='ASC'): def dates(self, field_name, kind, order='ASC'):
""" """
Returns a list of datetime objects representing all available dates Returns a list of datetime objects representing all available dates for
for the given field_name, scoped to 'kind'. the given field_name, scoped to 'kind'.
""" """
assert kind in ("month", "year", "day"), \ assert kind in ("month", "year", "day"), \
"'kind' must be one of 'year', 'month' or 'day'." "'kind' must be one of 'year', 'month' or 'day'."
@ -441,7 +448,7 @@ class QuerySet(object):
def none(self): def none(self):
""" """
Returns an empty queryset. Returns an empty QuerySet.
""" """
return self._clone(klass=EmptyQuerySet) return self._clone(klass=EmptyQuerySet)
@ -485,6 +492,7 @@ class QuerySet(object):
def complex_filter(self, filter_obj): def complex_filter(self, filter_obj):
""" """
Returns a new QuerySet instance with filter_obj added to the filters. Returns a new QuerySet instance with filter_obj added to the filters.
filter_obj can be a Q object (or anything with an add_to_query() filter_obj can be a Q object (or anything with an add_to_query()
method) or a dictionary of keyword lookup arguments. method) or a dictionary of keyword lookup arguments.
@ -500,8 +508,9 @@ class QuerySet(object):
def select_related(self, *fields, **kwargs): def select_related(self, *fields, **kwargs):
""" """
Returns a new QuerySet instance that will select related objects. If Returns a new QuerySet instance that will select related objects.
fields are specified, they must be ForeignKey fields and only those
If fields are specified, they must be ForeignKey fields and only those
related objects are included in the selection. related objects are included in the selection.
""" """
depth = kwargs.pop('depth', 0) depth = kwargs.pop('depth', 0)
@ -521,13 +530,15 @@ class QuerySet(object):
def dup_select_related(self, other): def dup_select_related(self, other):
""" """
Copies the related selection status from the queryset 'other' to the Copies the related selection status from the QuerySet 'other' to the
current queryset. current QuerySet.
""" """
self.query.select_related = other.query.select_related self.query.select_related = other.query.select_related
def order_by(self, *field_names): def order_by(self, *field_names):
"""Returns a new QuerySet instance with the ordering changed.""" """
Returns a new QuerySet instance with the ordering changed.
"""
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot reorder a query once a slice has been taken." "Cannot reorder a query once a slice has been taken."
obj = self._clone() obj = self._clone()
@ -544,9 +555,9 @@ class QuerySet(object):
return obj return obj
def extra(self, select=None, where=None, params=None, tables=None, def extra(self, select=None, where=None, params=None, tables=None,
order_by=None, select_params=None): order_by=None, select_params=None):
""" """
Add extra SQL fragments to the query. Adds extra SQL fragments to the query.
""" """
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot change a query once a slice has been taken" "Cannot change a query once a slice has been taken"
@ -556,7 +567,7 @@ class QuerySet(object):
def reverse(self): def reverse(self):
""" """
Reverses the ordering of the queryset. Reverses the ordering of the QuerySet.
""" """
clone = self._clone() clone = self._clone()
clone.query.standard_ordering = not clone.query.standard_ordering clone.query.standard_ordering = not clone.query.standard_ordering
@ -589,12 +600,13 @@ class QuerySet(object):
def _merge_sanity_check(self, other): def _merge_sanity_check(self, other):
""" """
Checks that we are merging two comparable queryset classes. By default Checks that we are merging two comparable QuerySet classes. By default
this does nothing, but see the ValuesQuerySet for an example of where this does nothing, but see the ValuesQuerySet for an example of where
it's useful. it's useful.
""" """
pass pass
class ValuesQuerySet(QuerySet): class ValuesQuerySet(QuerySet):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ValuesQuerySet, self).__init__(*args, **kwargs) super(ValuesQuerySet, self).__init__(*args, **kwargs)
@ -617,7 +629,7 @@ class ValuesQuerySet(QuerySet):
Constructs the field_names list that the values query will be Constructs the field_names list that the values query will be
retrieving. retrieving.
Called by the _clone() method after initialising the rest of the Called by the _clone() method after initializing the rest of the
instance. instance.
""" """
self.extra_names = [] self.extra_names = []
@ -658,6 +670,7 @@ class ValuesQuerySet(QuerySet):
raise TypeError("Merging '%s' classes must involve the same values in each case." raise TypeError("Merging '%s' classes must involve the same values in each case."
% self.__class__.__name__) % self.__class__.__name__)
class ValuesListQuerySet(ValuesQuerySet): class ValuesListQuerySet(ValuesQuerySet):
def iterator(self): def iterator(self):
self.query.trim_extra_select(self.extra_names) self.query.trim_extra_select(self.extra_names)
@ -681,6 +694,7 @@ class ValuesListQuerySet(ValuesQuerySet):
clone.flat = self.flat clone.flat = self.flat
return clone return clone
class DateQuerySet(QuerySet): class DateQuerySet(QuerySet):
def iterator(self): def iterator(self):
return self.query.results_iter() return self.query.results_iter()
@ -689,7 +703,7 @@ class DateQuerySet(QuerySet):
""" """
Sets up any special features of the query attribute. Sets up any special features of the query attribute.
Called by the _clone() method after initialising the rest of the Called by the _clone() method after initializing the rest of the
instance. instance.
""" """
self.query = self.query.clone(klass=sql.DateQuery, setup=True) self.query = self.query.clone(klass=sql.DateQuery, setup=True)
@ -706,6 +720,7 @@ class DateQuerySet(QuerySet):
c._setup_query() c._setup_query()
return c return c
class EmptyQuerySet(QuerySet): class EmptyQuerySet(QuerySet):
def __init__(self, model=None, query=None): def __init__(self, model=None, query=None):
super(EmptyQuerySet, self).__init__(model, query) super(EmptyQuerySet, self).__init__(model, query)
@ -733,6 +748,7 @@ class EmptyQuerySet(QuerySet):
# (it raises StopIteration immediately). # (it raises StopIteration immediately).
yield iter([]).next() yield iter([]).next()
# QOperator, QNot, QAnd and QOr are temporarily retained for backwards # QOperator, QNot, QAnd and QOr are temporarily retained for backwards
# compatibility. All the old functionality is now part of the 'Q' class. # compatibility. All the old functionality is now part of the 'Q' class.
class QOperator(Q): class QOperator(Q):
@ -743,12 +759,14 @@ class QOperator(Q):
QOr = QAnd = QOperator QOr = QAnd = QOperator
def QNot(q): def QNot(q):
warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2) warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2)
return ~q return ~q
def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
requested=None): requested=None):
""" """
Helper function that recursively returns an object with the specified Helper function that recursively returns an object with the specified
related attributes already populated. related attributes already populated.
@ -774,6 +792,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
setattr(obj, f.get_cache_name(), rel_obj) setattr(obj, f.get_cache_name(), rel_obj)
return obj, index_end return obj, index_end
def delete_objects(seen_objs): def delete_objects(seen_objs):
""" """
Iterate through a list of seen classes, and remove any instances that are Iterate through a list of seen classes, and remove any instances that are
@ -782,10 +801,10 @@ def delete_objects(seen_objs):
try: try:
ordered_classes = seen_objs.keys() ordered_classes = seen_objs.keys()
except CyclicDependency: except CyclicDependency:
# if there is a cyclic dependency, we cannot in general delete # If there is a cyclic dependency, we cannot in general delete the
# the objects. However, if an appropriate transaction is set # objects. However, if an appropriate transaction is set up, or if the
# up, or if the database is lax enough, it will succeed. # database is lax enough, it will succeed. So for now, we go ahead and
# So for now, we go ahead and try anway. # try anyway.
ordered_classes = seen_objs.unordered_keys() ordered_classes = seen_objs.unordered_keys()
obj_pairs = {} obj_pairs = {}
@ -794,7 +813,7 @@ def delete_objects(seen_objs):
items.sort() items.sort()
obj_pairs[cls] = items obj_pairs[cls] = items
# Pre notify all instances to be deleted # Pre-notify all instances to be deleted.
for pk_val, instance in items: for pk_val, instance in items:
dispatcher.send(signal=signals.pre_delete, sender=cls, dispatcher.send(signal=signals.pre_delete, sender=cls,
instance=instance) instance=instance)
@ -808,7 +827,7 @@ def delete_objects(seen_objs):
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
update_query.clear_related(field, pk_list) update_query.clear_related(field, pk_list)
# Now delete the actual data # Now delete the actual data.
for cls in ordered_classes: for cls in ordered_classes:
items = obj_pairs[cls] items = obj_pairs[cls]
items.reverse() items.reverse()
@ -831,6 +850,7 @@ def delete_objects(seen_objs):
transaction.commit_unless_managed() transaction.commit_unless_managed()
def insert_query(model, values, return_id=False, raw_values=False): def insert_query(model, values, return_id=False, raw_values=False):
""" """
Inserts a new record for the given model. This provides an interface to Inserts a new record for the given model. This provides an interface to
@ -840,4 +860,3 @@ def insert_query(model, values, return_id=False, raw_values=False):
query = sql.InsertQuery(model, connection) query = sql.InsertQuery(model, connection)
query.insert_values(values, raw_values) query.insert_values(values, raw_values)
return query.execute_sql(return_id) return query.execute_sql(return_id)

View File

@ -136,6 +136,7 @@ class MultiPartParser(object):
# since we cannot be sure a file is complete until # since we cannot be sure a file is complete until
# we hit the next boundary/part of the multipart content. # we hit the next boundary/part of the multipart content.
self.handle_file_complete(old_field_name, counters) self.handle_file_complete(old_field_name, counters)
old_field_name = None
try: try:
disposition = meta_data['content-disposition'][1] disposition = meta_data['content-disposition'][1]

View File

@ -31,3 +31,54 @@ def conditional_content_removal(request, response):
if request.method == 'HEAD': if request.method == 'HEAD':
response.content = '' response.content = ''
return response return response
def fix_IE_for_attach(request, response):
"""
This function will prevent Django from serving a Content-Disposition header
while expecting the browser to cache it (only when the browser is IE). This
leads to IE not allowing the client to download.
"""
if 'MSIE' not in request.META.get('HTTP_USER_AGENT', '').upper():
return response
offending_headers = ('no-cache', 'no-store')
if response.has_header('Content-Disposition'):
try:
del response['Pragma']
except KeyError:
pass
if response.has_header('Cache-Control'):
cache_control_values = [value.strip() for value in
response['Cache-Control'].split(',')
if value.strip().lower() not in offending_headers]
if not len(cache_control_values):
del response['Cache-Control']
else:
response['Cache-Control'] = ', '.join(cache_control_values)
return response
def fix_IE_for_vary(request, response):
"""
This function will fix the bug reported at
http://support.microsoft.com/kb/824847/en-us?spid=8722&sid=global
by clearing the Vary header whenever the mime-type is not safe
enough for Internet Explorer to handle. Poor thing.
"""
if 'MSIE' not in request.META.get('HTTP_USER_AGENT', '').upper():
return response
# These mime-types that are decreed "Vary-safe" for IE:
safe_mime_types = ('text/html', 'text/plain', 'text/sgml')
# The first part of the Content-Type field will be the MIME type,
# everything after ';', such as character-set, can be ignored.
if response['Content-Type'].split(';')[0] not in safe_mime_types:
try:
del response['Vary']
except KeyError:
pass
return response

View File

@ -27,7 +27,7 @@ from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
from util import ErrorList, ValidationError from util import ErrorList, ValidationError
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
__all__ = ( __all__ = (
'Field', 'CharField', 'IntegerField', 'Field', 'CharField', 'IntegerField',
@ -419,18 +419,6 @@ except ImportError:
# It's OK if Django settings aren't configured. # It's OK if Django settings aren't configured.
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
class UploadedFile(StrAndUnicode):
"A wrapper for files uploaded in a FileField"
def __init__(self, filename, data):
self.filename = filename
self.data = data
def __unicode__(self):
"""
The unicode representation is the filename, so that the pre-database-insertion
logic can use UploadedFile objects
"""
return self.filename
class FileField(Field): class FileField(Field):
widget = FileInput widget = FileInput
@ -460,23 +448,20 @@ class FileField(Field):
category = DeprecationWarning, category = DeprecationWarning,
stacklevel = 2 stacklevel = 2
) )
data = UploadedFile(data['filename'], data['content'])
try: try:
file_name = data.file_name file_name = data.name
file_size = data.file_size file_size = data.size
except AttributeError: except AttributeError:
try: raise ValidationError(self.error_messages['invalid'])
file_name = data.get('filename')
file_size = bool(data['content'])
except (AttributeError, KeyError):
raise ValidationError(self.error_messages['invalid'])
if not file_name: if not file_name:
raise ValidationError(self.error_messages['invalid']) raise ValidationError(self.error_messages['invalid'])
if not file_size: if not file_size:
raise ValidationError(self.error_messages['empty']) raise ValidationError(self.error_messages['empty'])
return UploadedFile(file_name, data) return data
class ImageField(FileField): class ImageField(FileField):
default_error_messages = { default_error_messages = {

View File

@ -686,7 +686,7 @@ class FileUploadField(FormField):
if upload_errors: if upload_errors:
raise validators.CriticalValidationError, upload_errors raise validators.CriticalValidationError, upload_errors
try: try:
file_size = new_data.file_size file_size = new_data.size
except AttributeError: except AttributeError:
file_size = len(new_data['content']) file_size = len(new_data['content'])
if not file_size: if not file_size:

View File

@ -90,32 +90,34 @@ def encode_multipart(boundary, data):
""" """
lines = [] lines = []
to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET) to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET)
# Not by any means perfect, but good enough for our purposes.
is_file = lambda thing: hasattr(thing, "read") and callable(thing.read)
# Each bit of the multipart form data could be either a form value or a
# file, or a *list* of form values and/or files. Remember that HTTP field
# names can be duplicated!
for (key, value) in data.items(): for (key, value) in data.items():
if isinstance(value, file): if is_file(value):
lines.extend([ lines.extend(encode_file(boundary, key, value))
'--' + boundary, elif not isinstance(value, basestring) and is_iterable(value):
'Content-Disposition: form-data; name="%s"; filename="%s"' \ for item in value:
% (to_str(key), to_str(os.path.basename(value.name))), if is_file(item):
'Content-Type: application/octet-stream', lines.extend(encode_file(boundary, key, item))
'', else:
value.read()
])
else:
if not isinstance(value, basestring) and is_iterable(value):
for item in value:
lines.extend([ lines.extend([
'--' + boundary, '--' + boundary,
'Content-Disposition: form-data; name="%s"' % to_str(key), 'Content-Disposition: form-data; name="%s"' % to_str(key),
'', '',
to_str(item) to_str(item)
]) ])
else: else:
lines.extend([ lines.extend([
'--' + boundary, '--' + boundary,
'Content-Disposition: form-data; name="%s"' % to_str(key), 'Content-Disposition: form-data; name="%s"' % to_str(key),
'', '',
to_str(value) to_str(value)
]) ])
lines.extend([ lines.extend([
'--' + boundary + '--', '--' + boundary + '--',
@ -123,6 +125,17 @@ def encode_multipart(boundary, data):
]) ])
return '\r\n'.join(lines) return '\r\n'.join(lines)
def encode_file(boundary, key, file):
to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET)
return [
'--' + boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' \
% (to_str(key), to_str(os.path.basename(file.name))),
'Content-Type: application/octet-stream',
'',
file.read()
]
class Client: class Client:
""" """
A class that can act as a client for testing purposes. A class that can act as a client for testing purposes.

View File

@ -161,6 +161,15 @@ def translation(language):
res = _translation(globalpath) res = _translation(globalpath)
# We want to ensure that, for example, "en-gb" and "en-us" don't share
# the same translation object (thus, merging en-us with a local update
# doesn't affect en-gb), even though they will both use the core "en"
# translation. So we have to subvert Python's internal gettext caching.
base_lang = lambda x: x.split('-', 1)[0]
if base_lang(lang) in [base_lang(trans) for trans in _translations]:
res._info = res._info.copy()
res._catalog = res._catalog.copy()
def _merge(path): def _merge(path):
t = _translation(path) t = _translation(path)
if t is not None: if t is not None:

View File

@ -450,11 +450,11 @@ TECHNICAL_500_TEMPLATE = """
{% if frame.context_line %} {% if frame.context_line %}
<div class="context" id="c{{ frame.id }}"> <div class="context" id="c{{ frame.id }}">
{% if frame.pre_context %} {% if frame.pre_context %}
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol> <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
{% endif %} {% endif %}
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol> <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol>
{% if frame.post_context %} {% if frame.post_context %}
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol> <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}

View File

@ -1,7 +1,7 @@
from django.template import loader, RequestContext from django.template import loader, RequestContext
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.core.xheaders import populate_xheaders from django.core.xheaders import populate_xheaders
from django.core.paginator import QuerySetPaginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
def object_list(request, queryset, paginate_by=None, page=None, def object_list(request, queryset, paginate_by=None, page=None,
@ -45,7 +45,7 @@ def object_list(request, queryset, paginate_by=None, page=None,
if extra_context is None: extra_context = {} if extra_context is None: extra_context = {}
queryset = queryset._clone() queryset = queryset._clone()
if paginate_by: if paginate_by:
paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty) paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
if not page: if not page:
page = request.GET.get('page', 1) page = request.GET.get('page', 1)
try: try:

View File

@ -816,15 +816,14 @@ specify the page number in the URL in one of two ways:
These values and lists are 1-based, not 0-based, so the first page would be These values and lists are 1-based, not 0-based, so the first page would be
represented as page ``1``. represented as page ``1``.
An example of the use of pagination can be found in the `object pagination`_ For more on pagination, read the `pagination documentation`_.
example model.
.. _`object pagination`: ../models/pagination/ .. _`pagination documentation`: ../pagination/
**New in Django development version:** **New in Django development version:**
As a special case, you are also permitted to use As a special case, you are also permitted to use ``last`` as a value for
``last`` as a value for ``page``:: ``page``::
/objects/?page=last /objects/?page=last

View File

@ -1334,23 +1334,12 @@ given length.
* Validates that non-empty file data has been bound to the form. * Validates that non-empty file data has been bound to the form.
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty`` * Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
An ``UploadedFile`` object has two attributes: To learn more about the ``UploadedFile`` object, see the `file uploads documentation`_.
====================== ====================================================
Attribute Description
====================== ====================================================
``filename`` The name of the file, provided by the uploading
client.
``content`` The array of bytes comprising the file content.
====================== ====================================================
The string representation of an ``UploadedFile`` is the same as the filename
attribute.
When you use a ``FileField`` in a form, you must also remember to When you use a ``FileField`` in a form, you must also remember to
`bind the file data to the form`_. `bind the file data to the form`_.
.. _file uploads documentation: ../upload_handling/
.. _`bind the file data to the form`: `Binding uploaded files to a form`_ .. _`bind the file data to the form`: `Binding uploaded files to a form`_
``FilePathField`` ``FilePathField``

View File

@ -59,6 +59,11 @@ page::
... ...
InvalidPage InvalidPage
Note that you can give ``Paginator`` a list/tuple or a Django ``QuerySet``. The
only difference is in implementation; if you pass a ``QuerySet``, the
``Paginator`` will call its ``count()`` method instead of using ``len()``,
because the former is more efficient.
``Paginator`` objects ``Paginator`` objects
===================== =====================
@ -77,6 +82,21 @@ Attributes
``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. ``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
``InvalidPage`` exceptions
==========================
The ``page()`` method raises ``InvalidPage`` if the requested page is invalid
(i.e., not an integer) or contains no objects. Generally, it's enough to trap
the ``InvalidPage`` exception, but if you'd like more granularity, you can trap
either of the following exceptions:
``PageNotAnInteger`` -- Raised when ``page()`` is given a value that isn't an integer.
``EmptyPage`` -- Raised when ``page()`` is given a valid value but no objects exist on that page.
Both of the exceptions are subclasses of ``InvalidPage``, so you can handle
them both with a simple ``except InvalidPage``.
``Page`` objects ``Page`` objects
================ ================
@ -116,13 +136,6 @@ Attributes
``paginator`` -- The associated ``Paginator`` object. ``paginator`` -- The associated ``Paginator`` object.
``QuerySetPaginator`` objects
=============================
Use ``QuerySetPaginator`` instead of ``Paginator`` if you're paginating across
a ``QuerySet`` from Django's database API. This is slightly more efficient, and
there are no API differences between the two classes.
The legacy ``ObjectPaginator`` class The legacy ``ObjectPaginator`` class
==================================== ====================================

View File

@ -279,7 +279,7 @@ Default: ``''`` (Empty string)
The database backend to use. The build-in database backends are The database backend to use. The build-in database backends are
``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``,
``'sqlite3'``, ``'oracle'``, and ``'oracle'``. ``'sqlite3'``, and ``'oracle'``.
In the Django development version, you can use a database backend that doesn't In the Django development version, you can use a database backend that doesn't
ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e. ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e.

View File

@ -64,7 +64,7 @@ methods to access the uploaded content:
``UploadedFile.read()`` ``UploadedFile.read()``
Read the entire uploaded data from the file. Be careful with this Read the entire uploaded data from the file. Be careful with this
method: if the uploaded file is huge it can overwhelm your system if you method: if the uploaded file is huge it can overwhelm your system if you
try to read it into memory. You'll probably want to use ``chunk()`` try to read it into memory. You'll probably want to use ``chunks()``
instead; see below. instead; see below.
``UploadedFile.multiple_chunks()`` ``UploadedFile.multiple_chunks()``
@ -91,7 +91,7 @@ objects; see `UploadedFile objects`_ for a complete reference.
Putting it all together, here's a common way you might handle an uploaded file:: Putting it all together, here's a common way you might handle an uploaded file::
def handle_uploaded_file(f): def handle_uploaded_file(f):
destination = open('some/file/name.txt', 'wb') destination = open('some/file/name.txt', 'wb+')
for chunk in f.chunks(): for chunk in f.chunks():
destination.write(chunk) destination.write(chunk)
@ -161,13 +161,13 @@ All ``UploadedFile`` objects define the following methods/attributes:
Returns a byte string of length ``num_bytes``, or the complete file if Returns a byte string of length ``num_bytes``, or the complete file if
``num_bytes`` is ``None``. ``num_bytes`` is ``None``.
``UploadedFile.chunk(self, chunk_size=None)`` ``UploadedFile.chunks(self, chunk_size=None)``
A generator yielding small chunks from the file. If ``chunk_size`` isn't A generator yielding small chunks from the file. If ``chunk_size`` isn't
given, chunks will be 64 kb. given, chunks will be 64 KB.
``UploadedFile.multiple_chunks(self, chunk_size=None)`` ``UploadedFile.multiple_chunks(self, chunk_size=None)``
Returns ``True`` if you can expect more than one chunk when calling Returns ``True`` if you can expect more than one chunk when calling
``UploadedFile.chunk(self, chunk_size)``. ``UploadedFile.chunks(self, chunk_size)``.
``UploadedFile.file_size`` ``UploadedFile.file_size``
The size, in bytes, of the uploaded file. The size, in bytes, of the uploaded file.
@ -186,10 +186,14 @@ All ``UploadedFile`` objects define the following methods/attributes:
For ``text/*`` content-types, the character set (i.e. ``utf8``) supplied For ``text/*`` content-types, the character set (i.e. ``utf8``) supplied
by the browser. Again, "trust but verify" is the best policy here. by the browser. Again, "trust but verify" is the best policy here.
``UploadedFile.__iter__()``
Iterates over the lines in the file.
``UploadedFile.temporary_file_path()`` ``UploadedFile.temporary_file_path()``
Only files uploaded onto disk will have this method; it returns the full Only files uploaded onto disk will have this method; it returns the full
path to the temporary uploaded file. path to the temporary uploaded file.
Upload Handlers Upload Handlers
=============== ===============

View File

@ -803,7 +803,7 @@ False
>>> f.is_valid() >>> f.is_valid()
True True
>>> type(f.cleaned_data['file']) >>> type(f.cleaned_data['file'])
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
>>> instance = f.save() >>> instance = f.save()
>>> instance.file >>> instance.file
u'...test1.txt' u'...test1.txt'
@ -814,7 +814,7 @@ u'...test1.txt'
>>> f.is_valid() >>> f.is_valid()
True True
>>> type(f.cleaned_data['file']) >>> type(f.cleaned_data['file'])
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
>>> instance = f.save() >>> instance = f.save()
>>> instance.file >>> instance.file
u'...test1.txt' u'...test1.txt'
@ -906,7 +906,7 @@ u'...test3.txt'
>>> f.is_valid() >>> f.is_valid()
True True
>>> type(f.cleaned_data['image']) >>> type(f.cleaned_data['image'])
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
>>> instance = f.save() >>> instance = f.save()
>>> instance.image >>> instance.image
u'...test.png' u'...test.png'
@ -918,7 +918,7 @@ u'...test.png'
>>> f.is_valid() >>> f.is_valid()
True True
>>> type(f.cleaned_data['image']) >>> type(f.cleaned_data['image'])
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
>>> instance = f.save() >>> instance = f.save()
>>> instance.image >>> instance.image
u'...test.png' u'...test.png'

View File

@ -31,7 +31,7 @@ __test__ = {'API_TESTS':"""
# New/current API (Paginator/Page) # # New/current API (Paginator/Page) #
#################################### ####################################
>>> from django.core.paginator import Paginator, InvalidPage >>> from django.core.paginator import Paginator
>>> paginator = Paginator(Article.objects.all(), 5) >>> paginator = Paginator(Article.objects.all(), 5)
>>> paginator.count >>> paginator.count
9 9
@ -82,15 +82,15 @@ True
>>> p.end_index() >>> p.end_index()
9 9
# Invalid pages raise InvalidPage. # Empty pages raise EmptyPage.
>>> paginator.page(0) >>> paginator.page(0)
Traceback (most recent call last): Traceback (most recent call last):
... ...
InvalidPage: ... EmptyPage: ...
>>> paginator.page(3) >>> paginator.page(3)
Traceback (most recent call last): Traceback (most recent call last):
... ...
InvalidPage: ... EmptyPage: ...
# Empty paginators with allow_empty_first_page=True. # Empty paginators with allow_empty_first_page=True.
>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True) >>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True)
@ -148,7 +148,7 @@ True
>>> from warnings import filterwarnings >>> from warnings import filterwarnings
>>> filterwarnings("ignore") >>> filterwarnings("ignore")
>>> from django.core.paginator import ObjectPaginator, InvalidPage >>> from django.core.paginator import ObjectPaginator, EmptyPage
>>> paginator = ObjectPaginator(Article.objects.all(), 5) >>> paginator = ObjectPaginator(Article.objects.all(), 5)
>>> paginator.hits >>> paginator.hits
9 9
@ -181,15 +181,15 @@ True
>>> paginator.last_on_page(1) >>> paginator.last_on_page(1)
9 9
# Invalid pages raise InvalidPage. # Invalid pages raise EmptyPage.
>>> paginator.get_page(-1) >>> paginator.get_page(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
InvalidPage: ... EmptyPage: ...
>>> paginator.get_page(2) >>> paginator.get_page(2)
Traceback (most recent call last): Traceback (most recent call last):
... ...
InvalidPage: ... EmptyPage: ...
# Empty paginators with allow_empty_first_page=True. # Empty paginators with allow_empty_first_page=True.
>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5) >>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)

View File

@ -0,0 +1,10 @@
from django.core.management.base import AppCommand
class Command(AppCommand):
help = 'Test Application-based commands'
requires_model_validation = False
args = '[appname ...]'
def handle_app(self, app, **options):
print 'EXECUTE:AppCommand app=%s, options=%s' % (app, sorted(options.items()))

View File

@ -0,0 +1,9 @@
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Test basic commands'
requires_model_validation = False
args = '[labels ...]'
def handle(self, *labels, **options):
print 'EXECUTE:BaseCommand labels=%s, options=%s' % (labels, sorted(options.items()))

View File

@ -0,0 +1,9 @@
from django.core.management.base import LabelCommand
class Command(LabelCommand):
help = "Test Label-based commands"
requires_model_validation = False
args = '<label>'
def handle_label(self, label, **options):
print 'EXECUTE:LabelCommand label=%s, options=%s' % (label, sorted(options.items()))

View File

@ -0,0 +1,9 @@
from django.core.management.base import NoArgsCommand
class Command(NoArgsCommand):
help = "Test No-args commands"
requires_model_validation = False
def handle_noargs(self, **options):
print 'EXECUTE:NoArgsCommand options=%s' % sorted(options.items())

View File

@ -0,0 +1,12 @@
from django.db import models
class Article(models.Model):
headline = models.CharField(max_length=100, default='Default headline')
pub_date = models.DateTimeField()
def __unicode__(self):
return self.headline
class Meta:
ordering = ('-pub_date', 'headline')

View File

@ -0,0 +1,817 @@
"""
A series of tests to establish that the command-line managment tools work as
advertised - especially with regards to the handling of the DJANGO_SETTINGS_MODULE
and default settings.py files.
"""
import os
import unittest
import shutil
from django import conf, bin
from django.conf import settings
class AdminScriptTestCase(unittest.TestCase):
def write_settings(self, filename, apps=None):
test_dir = os.path.dirname(os.path.dirname(__file__))
settings_file = open(os.path.join(test_dir,filename), 'w')
settings_file.write('# Settings file automatically generated by regressiontests.admin_scripts test case\n')
exports = [
'DATABASE_ENGINE',
'DATABASE_NAME',
'DATABASE_USER',
'DATABASE_PASSWORD',
'DATABASE_HOST',
'DATABASE_PORT',
'ROOT_URLCONF'
]
for s in exports:
if hasattr(settings,s):
settings_file.write("%s = '%s'\n" % (s, str(getattr(settings,s))))
if apps is None:
apps = ['django.contrib.auth', 'django.contrib.contenttypes', 'regressiontests.admin_scripts']
if apps:
settings_file.write("INSTALLED_APPS = %s\n" % apps)
settings_file.close()
def remove_settings(self, filename):
test_dir = os.path.dirname(os.path.dirname(__file__))
os.remove(os.path.join(test_dir, filename))
# Also try to remove the pyc file; if it exists, it could
# mess up later tests that depend upon the .py file not existing
try:
os.remove(os.path.join(test_dir, filename + 'c'))
except OSError:
pass
def run_test(self, script, args, settings_file=None, apps=None):
test_dir = os.path.dirname(os.path.dirname(__file__))
project_dir = os.path.dirname(test_dir)
base_dir = os.path.dirname(project_dir)
# Build the command line
cmd = 'python "%s"' % script
cmd += ''.join(' %s' % arg for arg in args)
# Remember the old environment
old_django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', None)
old_python_path = os.environ.get('PYTHONPATH', None)
old_cwd = os.getcwd()
# Set the test environment
if settings_file:
os.environ['DJANGO_SETTINGS_MODULE'] = settings_file
elif 'DJANGO_SETTINGS_MODULE' in os.environ:
del os.environ['DJANGO_SETTINGS_MODULE']
os.environ['PYTHONPATH'] = os.pathsep.join([project_dir,base_dir])
# Move to the test directory and run
os.chdir(test_dir)
stdin, stdout, stderr = os.popen3(cmd)
out, err = stdout.read(), stderr.read()
# Restore the old environment
if old_django_settings_module:
os.environ['DJANGO_SETTINGS_MODULE'] = old_django_settings_module
if old_python_path:
os.environ['PYTHONPATH'] = old_python_path
# Move back to the old working directory
os.chdir(old_cwd)
return out, err
def run_django_admin(self, args, settings_file=None):
bin_dir = os.path.dirname(bin.__file__)
return self.run_test(os.path.join(bin_dir,'django-admin.py'), args, settings_file)
def run_manage(self, args, settings_file=None):
conf_dir = os.path.dirname(conf.__file__)
template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py')
test_dir = os.path.dirname(os.path.dirname(__file__))
test_manage_py = os.path.join(test_dir, 'manage.py')
shutil.copyfile(template_manage_py, test_manage_py)
stdout, stderr = self.run_test('./manage.py', args, settings_file)
# Cleanup - remove the generated manage.py script
os.remove(test_manage_py)
return stdout, stderr
def assertNoOutput(self, stream):
"Utility assertion: assert that the given stream is empty"
self.assertEquals(len(stream), 0, "Stream should be empty: actually contains '%s'" % stream)
def assertOutput(self, stream, msg):
"Utility assertion: assert that the given message exists in the output"
self.assertTrue(msg in stream, "'%s' does not match actual output text '%s'" % (msg, stream))
##########################################################################
# DJANGO ADMIN TESTS
# This first series of test classes checks the environment processing
# of the django-admin.py script
##########################################################################
class DjangoAdminNoSettings(AdminScriptTestCase):
"A series of tests for django-admin.py when there is no settings.py file."
def test_builtin_command(self):
"no settings: django-admin builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_bad_settings(self):
"no settings: django-admin builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_builtin_with_bad_environment(self):
"no settings: django-admin builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
class DjangoAdminDefaultSettings(AdminScriptTestCase):
"""A series of tests for django-admin.py when using a settings.py file that
contains the test application.
"""
def setUp(self):
self.write_settings('settings.py')
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"default: django-admin builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"default: django-admin builtin commands succeed if settings are provided as argument"
args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"default: django-admin builtin commands succeed if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"default: django-admin builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_builtin_with_bad_environment(self):
"default: django-admin builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_custom_command(self):
"default: django-admin can't execute user commands"
args = ['noargs_command']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"default: django-admin can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=regressiontests.settings']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_environment(self):
"default: django-admin can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_django_admin(args,'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
class DjangoAdminMinimalSettings(AdminScriptTestCase):
"""A series of tests for django-admin.py when using a settings.py file that
doesn't contain the test application.
"""
def setUp(self):
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"minimal: django-admin builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"minimal: django-admin builtin commands fail if settings are provided as argument"
args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_environment(self):
"minimal: django-admin builtin commands fail if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_bad_settings(self):
"minimal: django-admin builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_builtin_with_bad_environment(self):
"minimal: django-admin builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_custom_command(self):
"minimal: django-admin can't execute user commands"
args = ['noargs_command']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"minimal: django-admin can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=regressiontests.settings']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_environment(self):
"minimal: django-admin can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_django_admin(args,'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
class DjangoAdminAlternateSettings(AdminScriptTestCase):
"""A series of tests for django-admin.py when using a settings file
with a name other than 'settings.py'.
"""
def setUp(self):
self.write_settings('alternate_settings.py')
def tearDown(self):
self.remove_settings('alternate_settings.py')
def test_builtin_command(self):
"alternate: django-admin builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"alternate: django-admin builtin commands succeed if settings are provided as argument"
args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_builtin_with_bad_environment(self):
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_custom_command(self):
"alternate: django-admin can't execute user commands"
args = ['noargs_command']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"alternate: django-admin can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=regressiontests.alternate_settings']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_environment(self):
"alternate: django-admin can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
class DjangoAdminMultipleSettings(AdminScriptTestCase):
"""A series of tests for django-admin.py when multiple settings files
(including the default 'settings.py') are available. The default settings
file is insufficient for performing the operations described, so the
alternate settings must be used by the running script.
"""
def setUp(self):
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
self.write_settings('alternate_settings.py')
def tearDown(self):
self.remove_settings('settings.py')
self.remove_settings('alternate_settings.py')
def test_builtin_command(self):
"alternate: django-admin builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"alternate: django-admin builtin commands succeed if settings are provided as argument"
args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_builtin_with_bad_environment(self):
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_django_admin(args,'regressiontests.bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'")
def test_custom_command(self):
"alternate: django-admin can't execute user commands"
args = ['noargs_command']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"alternate: django-admin can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=regressiontests.alternate_settings']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_environment(self):
"alternate: django-admin can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
##########################################################################
# MANAGE.PY TESTS
# This next series of test classes checks the environment processing
# of the generated manage.py script
##########################################################################
class ManageNoSettings(AdminScriptTestCase):
"A series of tests for manage.py when there is no settings.py file."
def test_builtin_command(self):
"no settings: manage.py builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_bad_settings(self):
"no settings: manage.py builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_bad_environment(self):
"no settings: manage.py builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
class ManageDefaultSettings(AdminScriptTestCase):
"""A series of tests for manage.py when using a settings.py file that
contains the test application.
"""
def setUp(self):
self.write_settings('settings.py')
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"default: manage.py builtin commands succeed when default settings are appropriate"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_settings(self):
"default: manage.py builtin commands succeed if settings are provided as argument"
args = ['sqlall','--settings=settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"default: manage.py builtin commands succeed if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"default: manage.py builtin commands succeed if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"default: manage.py builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'bad_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_custom_command(self):
"default: manage.py can execute user commands when default settings are appropriate"
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
def test_custom_command_with_settings(self):
"default: manage.py can execute user commands when settings are provided as argument"
args = ['noargs_command', '--settings=settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
def test_custom_command_with_environment(self):
"default: manage.py can execute user commands when settings are provided in environment"
args = ['noargs_command']
out, err = self.run_manage(args,'settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
class ManageMinimalSettings(AdminScriptTestCase):
"""A series of tests for manage.py when using a settings.py file that
doesn't contain the test application.
"""
def setUp(self):
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"minimal: manage.py builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_settings(self):
"minimal: manage.py builtin commands fail if settings are provided as argument"
args = ['sqlall','--settings=settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_environment(self):
"minimal: manage.py builtin commands fail if settings are provided in the environment"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_bad_settings(self):
"minimal: manage.py builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"minimal: manage.py builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_custom_command(self):
"minimal: manage.py can't execute user commands without appropriate settings"
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"minimal: manage.py can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=settings']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_environment(self):
"minimal: manage.py can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_manage(args,'settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
class ManageAlternateSettings(AdminScriptTestCase):
"""A series of tests for manage.py when using a settings file
with a name other than 'settings.py'.
"""
def setUp(self):
self.write_settings('alternate_settings.py')
def tearDown(self):
self.remove_settings('alternate_settings.py')
def test_builtin_command(self):
"alternate: manage.py builtin commands fail with an import error when no default settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_settings(self):
"alternate: manage.py builtin commands fail if settings are provided as argument but no defaults"
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_environment(self):
"alternate: manage.py builtin commands fail if settings are provided in the environment but no defaults"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_bad_settings(self):
"alternate: manage.py builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_builtin_with_bad_environment(self):
"alternate: manage.py builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_custom_command(self):
"alternate: manage.py can't execute user commands"
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_custom_command_with_settings(self):
"alternate: manage.py can't execute user commands, even if settings are provided as argument"
args = ['noargs_command', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
def test_custom_command_with_environment(self):
"alternate: manage.py can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_manage(args,'alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
class ManageMultipleSettings(AdminScriptTestCase):
"""A series of tests for manage.py when multiple settings files
(including the default 'settings.py') are available. The default settings
file is insufficient for performing the operations described, so the
alternate settings must be used by the running script.
"""
def setUp(self):
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
self.write_settings('alternate_settings.py')
def tearDown(self):
self.remove_settings('settings.py')
self.remove_settings('alternate_settings.py')
def test_builtin_command(self):
"multiple: manage.py builtin commands fail with an import error when no settings provided"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found.')
def test_builtin_with_settings(self):
"multiple: manage.py builtin commands succeed if settings are provided as argument"
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"multiple: manage.py builtin commands fail if settings are provided in the environment"
# FIXME: This doesn't seem to be the correct output.
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found.')
def test_builtin_with_bad_settings(self):
"multiple: manage.py builtin commands fail if settings file (from argument) doesn't exist"
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"multiple: manage.py builtin commands fail if settings file (from environment) doesn't exist"
args = ['sqlall','admin_scripts']
out, err = self.run_manage(args,'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "App with label admin_scripts could not be found")
def test_custom_command(self):
"multiple: manage.py can't execute user commands using default settings"
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
def test_custom_command_with_settings(self):
"multiple: manage.py can execute user commands if settings are provided as argument"
args = ['noargs_command', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
def test_custom_command_with_environment(self):
"multiple: manage.py can execute user commands if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_manage(args,'alternate_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
##########################################################################
# COMMAND PROCESSING TESTS
# Check that user-space commands are correctly handled - in particular,
# that arguments to the commands are correctly parsed and processed.
##########################################################################
class CommandTypes(AdminScriptTestCase):
"Tests for the various types of base command types that can be defined."
def setUp(self):
self.write_settings('settings.py')
def tearDown(self):
self.remove_settings('settings.py')
def test_base_command(self):
"User BaseCommands can execute when a label is provided"
args = ['base_command','testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_base_command_no_label(self):
"User BaseCommands can execute when no labels are provided"
args = ['base_command']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_base_command_multiple_label(self):
"User BaseCommands can execute when no labels are provided"
args = ['base_command','testlabel','anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_noargs(self):
"NoArg Commands can be executed"
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_noargs_with_args(self):
"NoArg Commands raise an error if an argument is provided"
args = ['noargs_command','argument']
out, err = self.run_manage(args)
self.assertOutput(err, "Error: Command doesn't accept any arguments")
def test_app_command(self):
"User AppCommands can execute when a single app name is provided"
args = ['app_command', 'auth']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
self.assertOutput(out, os.sep.join(['django','contrib','auth','models.pyc']) + "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_app_command_no_apps(self):
"User AppCommands raise an error when no app name is provided"
args = ['app_command']
out, err = self.run_manage(args)
self.assertOutput(err, 'Error: Enter at least one appname.')
def test_app_command_multiple_apps(self):
"User AppCommands raise an error when multiple app names are provided"
args = ['app_command','auth','contenttypes']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
self.assertOutput(out, os.sep.join(['django','contrib','auth','models.pyc']) + "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.pyc']) + "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_app_command_invalid_appname(self):
"User AppCommands can execute when a single app name is provided"
args = ['app_command', 'NOT_AN_APP']
out, err = self.run_manage(args)
self.assertOutput(err, "App with label NOT_AN_APP could not be found")
def test_app_command_some_invalid_appnames(self):
"User AppCommands can execute when some of the provided app names are invalid"
args = ['app_command', 'auth', 'NOT_AN_APP']
out, err = self.run_manage(args)
self.assertOutput(err, "App with label NOT_AN_APP could not be found")
def test_label_command(self):
"User LabelCommands can execute when a label is provided"
args = ['label_command','testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
def test_label_command_no_label(self):
"User LabelCommands raise an error if no label is provided"
args = ['label_command']
out, err = self.run_manage(args)
self.assertOutput(err, 'Enter at least one label')
def test_label_command_multiple_label(self):
"User LabelCommands are executed multiple times if multiple labels are provided"
args = ['label_command','testlabel','anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")

View File

@ -156,3 +156,26 @@ class FileUploadTests(TestCase):
{'f': open(f.name)} {'f': open(f.name)}
) )
def test_fileupload_getlist(self):
file1 = tempfile.NamedTemporaryFile()
file1.write('a' * (2 ** 23))
file2 = tempfile.NamedTemporaryFile()
file2.write('a' * (2 * 2 ** 18))
file2a = tempfile.NamedTemporaryFile()
file2a.write('a' * (5 * 2 ** 20))
response = self.client.post('/file_uploads/getlist_count/', {
'file1': open(file1.name),
'field1': u'test',
'field2': u'test3',
'field3': u'test5',
'field4': u'test6',
'field5': u'test7',
'file2': (open(file2.name), open(file2a.name))
})
got = simplejson.loads(response.content)
self.assertEqual(got.get('file1'), 1)
self.assertEqual(got.get('file2'), 2)

View File

@ -7,4 +7,5 @@ urlpatterns = patterns('',
(r'^echo/$', views.file_upload_echo), (r'^echo/$', views.file_upload_echo),
(r'^quota/$', views.file_upload_quota), (r'^quota/$', views.file_upload_quota),
(r'^quota/broken/$', views.file_upload_quota_broken), (r'^quota/broken/$', views.file_upload_quota_broken),
(r'^getlist_count/$', views.file_upload_getlist_count),
) )

View File

@ -15,7 +15,7 @@ def file_upload_view(request):
if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode): if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode):
# If a file is posted, the dummy client should only post the file name, # If a file is posted, the dummy client should only post the file name,
# not the full path. # not the full path.
if os.path.dirname(form_data['file_field'].file_name) != '': if os.path.dirname(form_data['file_field'].name) != '':
return HttpResponseServerError() return HttpResponseServerError()
return HttpResponse('') return HttpResponse('')
else: else:
@ -29,7 +29,7 @@ def file_upload_view_verify(request):
form_data.update(request.FILES) form_data.update(request.FILES)
# Check to see if unicode names worked out. # Check to see if unicode names worked out.
if not request.FILES['file_unicode'].file_name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'): if not request.FILES['file_unicode'].name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'):
return HttpResponseServerError() return HttpResponseServerError()
for key, value in form_data.items(): for key, value in form_data.items():
@ -51,7 +51,7 @@ def file_upload_echo(request):
""" """
Simple view to echo back info about uploaded files for tests. Simple view to echo back info about uploaded files for tests.
""" """
r = dict([(k, f.file_name) for k, f in request.FILES.items()]) r = dict([(k, f.name) for k, f in request.FILES.items()])
return HttpResponse(simplejson.dumps(r)) return HttpResponse(simplejson.dumps(r))
def file_upload_quota(request): def file_upload_quota(request):
@ -68,3 +68,13 @@ def file_upload_quota_broken(request):
response = file_upload_echo(request) response = file_upload_echo(request)
request.upload_handlers.insert(0, QuotaUploadHandler()) request.upload_handlers.insert(0, QuotaUploadHandler())
return response return response
def file_upload_getlist_count(request):
"""
Check the .getlist() function to ensure we receive the correct number of files.
"""
file_counts = {}
for key in request.FILES.keys():
file_counts[key] = len(request.FILES.getlist(key))
return HttpResponse(simplejson.dumps(file_counts))

View File

@ -800,10 +800,10 @@ Traceback (most recent call last):
ValidationError: [u'The submitted file is empty.'] ValidationError: [u'The submitted file is empty.']
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'))) >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content')))
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf')) >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf'))
<class 'django.newforms.fields.UploadedFile'> <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
# URLField ################################################################## # URLField ##################################################################