mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +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:
parent
9cbd07b681
commit
60c060c476
1
AUTHORS
1
AUTHORS
@ -287,6 +287,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Neal Norwitz <nnorwitz@google.com>
|
||||
Todd O'Bryan <toddobryan@mac.com>
|
||||
oggie rob <oz.robharvey@gmail.com>
|
||||
oggy <ognjen.maric@gmail.com>
|
||||
Jay Parlar <parlar@gmail.com>
|
||||
Carlos Eduardo de Paula <carlosedp@gmail.com>
|
||||
pavithran s <pavithran.s@gmail.com>
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.admin.filterspecs import FilterSpec
|
||||
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.models.query import QuerySet
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
@ -109,8 +109,7 @@ class ChangeList(object):
|
||||
return '?%s' % urlencode(p)
|
||||
|
||||
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.
|
||||
try:
|
||||
result_count = paginator.count
|
||||
|
@ -3,12 +3,40 @@ Classes representing uploaded files.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import warnings
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
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):
|
||||
"""
|
||||
@ -20,16 +48,19 @@ class UploadedFile(object):
|
||||
"""
|
||||
DEFAULT_CHUNK_SIZE = 64 * 2**10
|
||||
|
||||
def __init__(self, file_name=None, content_type=None, file_size=None, charset=None):
|
||||
self.file_name = file_name
|
||||
self.file_size = file_size
|
||||
def __init__(self, name=None, content_type=None, size=None, charset=None):
|
||||
self.name = name
|
||||
self.size = size
|
||||
self.content_type = content_type
|
||||
self.charset = charset
|
||||
|
||||
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.
|
||||
if name is not None:
|
||||
# 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 = name[:255 - len(ext)] + ext
|
||||
|
||||
self._file_name = name
|
||||
self._name = name
|
||||
|
||||
def _get_file_name(self):
|
||||
return self._file_name
|
||||
name = property(_get_name, _set_name)
|
||||
|
||||
file_name = property(_get_file_name, _set_file_name)
|
||||
|
||||
def chunk(self, chunk_size=None):
|
||||
def chunks(self, chunk_size=None):
|
||||
"""
|
||||
Read the file and yield chucks of ``chunk_size`` bytes (defaults to
|
||||
``UploadedFile.DEFAULT_CHUNK_SIZE``).
|
||||
@ -58,12 +86,27 @@ class UploadedFile(object):
|
||||
if hasattr(self, 'seek'):
|
||||
self.seek(0)
|
||||
# Assume the pointer is at zero...
|
||||
counter = self.file_size
|
||||
counter = self.size
|
||||
|
||||
while counter > 0:
|
||||
yield self.read(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):
|
||||
"""
|
||||
Returns ``True`` if you can expect multiple chunks.
|
||||
@ -74,9 +117,9 @@ class UploadedFile(object):
|
||||
"""
|
||||
if not 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.
|
||||
def read(self, num_bytes=None):
|
||||
raise NotImplementedError()
|
||||
@ -87,23 +130,49 @@ class UploadedFile(object):
|
||||
def close(self):
|
||||
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.
|
||||
def __getitem__(self, key):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.",
|
||||
category = DeprecationWarning,
|
||||
stacklevel = 2
|
||||
)
|
||||
backwards_translate = {
|
||||
'filename': 'file_name',
|
||||
'filename': 'name',
|
||||
'content-type': 'content_type',
|
||||
}
|
||||
}
|
||||
|
||||
if key == 'content':
|
||||
return self.read()
|
||||
elif key == 'filename':
|
||||
return self.file_name
|
||||
return self.name
|
||||
elif key == 'content-type':
|
||||
return self.content_type
|
||||
else:
|
||||
@ -113,34 +182,36 @@ class TemporaryUploadedFile(UploadedFile):
|
||||
"""
|
||||
A file uploaded to a temporary location (i.e. stream-to-disk).
|
||||
"""
|
||||
|
||||
def __init__(self, file, file_name, content_type, file_size, charset):
|
||||
super(TemporaryUploadedFile, self).__init__(file_name, content_type, file_size, charset)
|
||||
self.file = file
|
||||
self.path = file.name
|
||||
self.file.seek(0)
|
||||
def __init__(self, name, content_type, size, charset):
|
||||
super(TemporaryUploadedFile, self).__init__(name, content_type, size, charset)
|
||||
if settings.FILE_UPLOAD_TEMP_DIR:
|
||||
self._file = tempfile.NamedTemporaryFile(suffix='.upload', dir=settings.FILE_UPLOAD_TEMP_DIR)
|
||||
else:
|
||||
self._file = tempfile.NamedTemporaryFile(suffix='.upload')
|
||||
|
||||
def temporary_file_path(self):
|
||||
"""
|
||||
Returns the full path of this file.
|
||||
"""
|
||||
return self.path
|
||||
return self.name
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
return self.file.read(*args, **kwargs)
|
||||
|
||||
def open(self):
|
||||
self.seek(0)
|
||||
|
||||
def seek(self, *args, **kwargs):
|
||||
self.file.seek(*args, **kwargs)
|
||||
# Most methods on this object get proxied to NamedTemporaryFile.
|
||||
# We can't directly subclass because NamedTemporaryFile is actually a
|
||||
# factory function
|
||||
def read(self, *args): return self._file.read(*args)
|
||||
def seek(self, offset): return self._file.seek(offset)
|
||||
def write(self, s): return self._file.write(s)
|
||||
def close(self): return self._file.close()
|
||||
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):
|
||||
"""
|
||||
A file uploaded into memory (i.e. stream-to-memory).
|
||||
"""
|
||||
def __init__(self, file, field_name, file_name, content_type, file_size, charset):
|
||||
super(InMemoryUploadedFile, self).__init__(file_name, content_type, file_size, charset)
|
||||
def __init__(self, file, field_name, name, content_type, size, charset):
|
||||
super(InMemoryUploadedFile, self).__init__(name, content_type, size, charset)
|
||||
self.file = file
|
||||
self.field_name = field_name
|
||||
self.file.seek(0)
|
||||
@ -154,7 +225,7 @@ class InMemoryUploadedFile(UploadedFile):
|
||||
def read(self, *args, **kwargs):
|
||||
return self.file.read(*args, **kwargs)
|
||||
|
||||
def chunk(self, chunk_size=None):
|
||||
def chunks(self, chunk_size=None):
|
||||
self.file.seek(0)
|
||||
yield self.read()
|
||||
|
||||
@ -168,9 +239,9 @@ class SimpleUploadedFile(InMemoryUploadedFile):
|
||||
"""
|
||||
def __init__(self, name, content, content_type='text/plain'):
|
||||
self.file = StringIO(content or '')
|
||||
self.file_name = name
|
||||
self.name = name
|
||||
self.field_name = None
|
||||
self.file_size = len(content or '')
|
||||
self.size = len(content or '')
|
||||
self.content_type = content_type
|
||||
self.charset = None
|
||||
self.file.seek(0)
|
||||
|
@ -132,21 +132,15 @@ class TemporaryFileUploadHandler(FileUploadHandler):
|
||||
Create the file object to append to as data is coming in.
|
||||
"""
|
||||
super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs)
|
||||
self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR)
|
||||
self.write = self.file.write
|
||||
self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset)
|
||||
|
||||
def receive_data_chunk(self, raw_data, start):
|
||||
self.write(raw_data)
|
||||
self.file.write(raw_data)
|
||||
|
||||
def file_complete(self, file_size):
|
||||
self.file.seek(0)
|
||||
return TemporaryUploadedFile(
|
||||
file = self.file,
|
||||
file_name = self.file_name,
|
||||
content_type = self.content_type,
|
||||
file_size = file_size,
|
||||
charset = self.charset
|
||||
)
|
||||
self.file.size = file_size
|
||||
return self.file
|
||||
|
||||
class MemoryFileUploadHandler(FileUploadHandler):
|
||||
"""
|
||||
@ -189,37 +183,12 @@ class MemoryFileUploadHandler(FileUploadHandler):
|
||||
return InMemoryUploadedFile(
|
||||
file = self.file,
|
||||
field_name = self.field_name,
|
||||
file_name = self.file_name,
|
||||
name = self.file_name,
|
||||
content_type = self.content_type,
|
||||
file_size = file_size,
|
||||
size = file_size,
|
||||
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):
|
||||
"""
|
||||
|
@ -6,8 +6,12 @@ from django.dispatch import dispatcher
|
||||
|
||||
class BaseHandler(object):
|
||||
# Changes that are always applied to a response (in this order).
|
||||
response_fixes = [http.fix_location_header,
|
||||
http.conditional_content_removal]
|
||||
response_fixes = [
|
||||
http.fix_location_header,
|
||||
http.conditional_content_removal,
|
||||
http.fix_IE_for_attach,
|
||||
http.fix_IE_for_vary,
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
|
||||
|
@ -205,10 +205,12 @@ class EmailMessage(object):
|
||||
conversions.
|
||||
"""
|
||||
if to:
|
||||
assert not isinstance(to, basestring), '"to" argument must be a list or tuple'
|
||||
self.to = list(to)
|
||||
else:
|
||||
self.to = []
|
||||
if bcc:
|
||||
assert not isinstance(bcc, basestring), '"bcc" argument must be a list or tuple'
|
||||
self.bcc = list(bcc)
|
||||
else:
|
||||
self.bcc = []
|
||||
|
@ -1,6 +1,12 @@
|
||||
class InvalidPage(Exception):
|
||||
pass
|
||||
|
||||
class PageNotAnInteger(InvalidPage):
|
||||
pass
|
||||
|
||||
class EmptyPage(InvalidPage):
|
||||
pass
|
||||
|
||||
class Paginator(object):
|
||||
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
|
||||
self.object_list = object_list
|
||||
@ -14,14 +20,14 @@ class Paginator(object):
|
||||
try:
|
||||
number = int(number)
|
||||
except ValueError:
|
||||
raise InvalidPage('That page number is not an integer')
|
||||
raise PageNotAnInteger('That page number is not an integer')
|
||||
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 == 1 and self.allow_empty_first_page:
|
||||
pass
|
||||
else:
|
||||
raise InvalidPage('That page contains no results')
|
||||
raise EmptyPage('That page contains no results')
|
||||
return number
|
||||
|
||||
def page(self, number):
|
||||
@ -36,7 +42,11 @@ class Paginator(object):
|
||||
def _get_count(self):
|
||||
"Returns the total number of objects, across all pages."
|
||||
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
|
||||
count = property(_get_count)
|
||||
|
||||
@ -61,15 +71,7 @@ class Paginator(object):
|
||||
return range(1, self.num_pages + 1)
|
||||
page_range = property(_get_page_range)
|
||||
|
||||
class QuerySetPaginator(Paginator):
|
||||
"""
|
||||
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)
|
||||
QuerySetPaginator = Paginator # For backwards-compatibility.
|
||||
|
||||
class Page(object):
|
||||
def __init__(self, object_list, number, paginator):
|
||||
@ -133,14 +135,14 @@ class ObjectPaginator(Paginator):
|
||||
try:
|
||||
page_number = int(page_number) + 1
|
||||
except ValueError:
|
||||
raise InvalidPage
|
||||
raise PageNotAnInteger
|
||||
return self.validate_number(page_number)
|
||||
|
||||
def get_page(self, page_number):
|
||||
try:
|
||||
page_number = int(page_number) + 1
|
||||
except ValueError:
|
||||
raise InvalidPage
|
||||
raise PageNotAnInteger
|
||||
return self.page(page_number).object_list
|
||||
|
||||
def has_next_page(self, page_number):
|
||||
|
@ -3,6 +3,10 @@ import types
|
||||
import sys
|
||||
import os
|
||||
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.manager # Ditto.
|
||||
@ -23,13 +27,11 @@ from django.core.files.move import file_move_safe
|
||||
from django.core.files import locks
|
||||
from django.conf import settings
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
class ModelBase(type):
|
||||
"Metaclass for all models"
|
||||
"""
|
||||
Metaclass for all models.
|
||||
"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
super_new = super(ModelBase, cls).__new__
|
||||
parents = [b for b in bases if isinstance(b, ModelBase)]
|
||||
@ -141,7 +143,9 @@ class ModelBase(type):
|
||||
setattr(cls, name, value)
|
||||
|
||||
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._prepare(cls)
|
||||
|
||||
@ -160,6 +164,7 @@ class ModelBase(type):
|
||||
|
||||
dispatcher.send(signal=signals.class_prepared, sender=cls)
|
||||
|
||||
|
||||
class Model(object):
|
||||
__metaclass__ = ModelBase
|
||||
|
||||
@ -264,7 +269,7 @@ class Model(object):
|
||||
|
||||
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.
|
||||
"""
|
||||
self.save_base()
|
||||
@ -368,10 +373,12 @@ class Model(object):
|
||||
|
||||
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:
|
||||
[(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()
|
||||
if seen_objs.add(self.__class__, pk_val, self, parent, nullable):
|
||||
@ -408,11 +415,11 @@ class Model(object):
|
||||
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)
|
||||
|
||||
# Find all the objects than need to be deleted
|
||||
# Find all the objects than need to be deleted.
|
||||
seen_objs = CollectedObjects()
|
||||
self._collect_sub_objects(seen_objs)
|
||||
|
||||
# Actually delete the objects
|
||||
# Actually delete the objects.
|
||||
delete_objects(seen_objs)
|
||||
|
||||
delete.alters_data = True
|
||||
@ -451,12 +458,12 @@ class Model(object):
|
||||
return getattr(self, cachename)
|
||||
|
||||
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 ''
|
||||
|
||||
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
|
||||
return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
|
||||
return ''
|
||||
@ -471,16 +478,14 @@ class Model(object):
|
||||
except OSError: # Directory probably already exists.
|
||||
pass
|
||||
|
||||
#
|
||||
# Check for old-style usage (files-as-dictionaries). Warn here first
|
||||
# since there are multiple locations where we need to support both new
|
||||
# and old usage.
|
||||
#
|
||||
if isinstance(raw_field, dict):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
message = "Representing uploaded files as dictionaries is"\
|
||||
" deprected. Use django.core.files.SimpleUploadedFile"\
|
||||
" deprecated. Use django.core.files.SimpleUploadedFile"\
|
||||
" instead.",
|
||||
category = DeprecationWarning,
|
||||
stacklevel = 2
|
||||
@ -505,41 +510,35 @@ class Model(object):
|
||||
|
||||
filename = field.get_filename(filename)
|
||||
|
||||
#
|
||||
# If the filename already exists, keep adding an underscore to the name of
|
||||
# the file until the filename doesn't exist.
|
||||
#
|
||||
# If the filename already exists, keep adding an underscore to the name
|
||||
# of the file until the filename doesn't exist.
|
||||
while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
|
||||
try:
|
||||
dot_index = filename.rindex('.')
|
||||
except ValueError: # filename has no dot
|
||||
except ValueError: # filename has no dot.
|
||||
filename += '_'
|
||||
else:
|
||||
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)
|
||||
|
||||
full_filename = self._get_FIELD_filename(field)
|
||||
|
||||
if hasattr(raw_field, 'temporary_file_path'):
|
||||
# This file has a file path that we can move.
|
||||
raw_field.close()
|
||||
file_move_safe(raw_field.temporary_file_path(), full_filename)
|
||||
|
||||
else:
|
||||
# This is a normal uploadedfile that we can stream.
|
||||
fp = open(full_filename, 'wb')
|
||||
locks.lock(fp, locks.LOCK_EX)
|
||||
for chunk in raw_field.chunk():
|
||||
for chunk in raw_field.chunks():
|
||||
fp.write(chunk)
|
||||
locks.unlock(fp)
|
||||
fp.close()
|
||||
|
||||
# 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
|
||||
width, height = get_image_dimensions(full_filename)
|
||||
if field.width_field:
|
||||
@ -547,7 +546,7 @@ class Model(object):
|
||||
if field.height_field:
|
||||
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:
|
||||
self.save()
|
||||
|
||||
@ -567,6 +566,7 @@ class Model(object):
|
||||
setattr(self, cachename, get_image_dimensions(filename))
|
||||
return getattr(self, cachename)
|
||||
|
||||
|
||||
############################################
|
||||
# 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)
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
|
||||
def method_get_order(ordered_obj, self):
|
||||
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_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
|
||||
ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)]
|
||||
|
||||
|
||||
##############################################
|
||||
# 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):
|
||||
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)
|
||||
|
||||
|
||||
########
|
||||
# MISC #
|
||||
########
|
||||
@ -607,8 +610,6 @@ if sys.version_info < (2, 5):
|
||||
# Prior to Python 2.5, Exception was an old-style class
|
||||
def subclass_exception(name, parent, unused):
|
||||
return types.ClassType(name, (parent,), {})
|
||||
|
||||
else:
|
||||
def subclass_exception(name, parent, module):
|
||||
return type(name, (parent,), {'__module__': module})
|
||||
|
||||
|
@ -751,9 +751,12 @@ class FileField(Field):
|
||||
def get_db_prep_save(self, value):
|
||||
"Returns field's value prepared for saving into a database."
|
||||
# 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 unicode(value)
|
||||
else:
|
||||
return unicode(value)
|
||||
|
||||
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)
|
||||
@ -827,7 +830,7 @@ class FileField(Field):
|
||||
# We don't need to raise a warning because Model._save_FIELD_file will
|
||||
# do so for us.
|
||||
try:
|
||||
file_name = file.file_name
|
||||
file_name = file.name
|
||||
except AttributeError:
|
||||
file_name = file['filename']
|
||||
|
||||
@ -842,9 +845,9 @@ class FileField(Field):
|
||||
return os.path.normpath(f)
|
||||
|
||||
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):
|
||||
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):
|
||||
defaults = {'form_class': forms.FileField}
|
||||
|
@ -13,19 +13,20 @@ from django.utils.datastructures import SortedDict
|
||||
CHUNK_SIZE = 100
|
||||
ITER_CHUNK_SIZE = CHUNK_SIZE
|
||||
|
||||
# Pull into this namespace for backwards compatibility
|
||||
# Pull into this namespace for backwards compatibility.
|
||||
EmptyResultSet = sql.EmptyResultSet
|
||||
|
||||
class CyclicDependency(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CollectedObjects(object):
|
||||
"""
|
||||
A container that stores keys and lists of values along with
|
||||
remembering the parent objects for all the keys.
|
||||
A container that stores keys and lists of values along with remembering the
|
||||
parent objects for all the keys.
|
||||
|
||||
This is used for the database object deletion routines so that we
|
||||
can calculate the 'leaf' objects which should be deleted first.
|
||||
This is used for the database object deletion routines so that we can
|
||||
calculate the 'leaf' objects which should be deleted first.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@ -34,26 +35,27 @@ class CollectedObjects(object):
|
||||
|
||||
def add(self, model, pk, obj, parent_model, nullable=False):
|
||||
"""
|
||||
Adds an item.
|
||||
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.
|
||||
Adds an item to the container.
|
||||
|
||||
If the item already existed in the structure,
|
||||
returns true, otherwise false.
|
||||
Arguments:
|
||||
* 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())
|
||||
retval = pk in d
|
||||
d[pk] = obj
|
||||
# Nullable relationships can be ignored -- they
|
||||
# are nulled out before deleting, and therefore
|
||||
# do not affect the order in which objects have
|
||||
# to be deleted.
|
||||
# Nullable relationships can be ignored -- they are nulled out before
|
||||
# deleting, and therefore do not affect the order in which objects
|
||||
# have to be deleted.
|
||||
if parent_model is not None and not nullable:
|
||||
self.children.setdefault(parent_model, []).append(model)
|
||||
|
||||
return retval
|
||||
|
||||
def __contains__(self, key):
|
||||
@ -77,8 +79,8 @@ class CollectedObjects(object):
|
||||
|
||||
def ordered_keys(self):
|
||||
"""
|
||||
Returns the models in the order that they should be
|
||||
dealth with i.e. models with no dependencies first.
|
||||
Returns the models in the order that they should be dealt with (i.e.
|
||||
models with no dependencies first).
|
||||
"""
|
||||
dealt_with = SortedDict()
|
||||
# Start with items that have no children
|
||||
@ -91,19 +93,22 @@ class CollectedObjects(object):
|
||||
dealt_with[model] = None
|
||||
found = True
|
||||
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()
|
||||
|
||||
def unordered_keys(self):
|
||||
"""
|
||||
Fallback for the case where is a cyclic dependency but we
|
||||
don't care.
|
||||
Fallback for the case where is a cyclic dependency but we don't care.
|
||||
"""
|
||||
return self.data.keys()
|
||||
|
||||
|
||||
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):
|
||||
self.model = model
|
||||
self.query = query or sql.Query(self.model, connection)
|
||||
@ -116,7 +121,7 @@ class QuerySet(object):
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Allows the Queryset to be pickled.
|
||||
Allows the QuerySet to be pickled.
|
||||
"""
|
||||
# Force the cache to be fully populated.
|
||||
len(self)
|
||||
@ -131,7 +136,7 @@ class QuerySet(object):
|
||||
def __len__(self):
|
||||
# Since __len__ is called quite frequently (for example, as part of
|
||||
# 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._iter:
|
||||
self._result_cache = list(self._iter)
|
||||
@ -173,7 +178,9 @@ class QuerySet(object):
|
||||
return True
|
||||
|
||||
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)):
|
||||
raise TypeError
|
||||
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
|
||||
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
|
||||
SELECT COUNT(*) calls.
|
||||
"""
|
||||
@ -290,7 +297,7 @@ class QuerySet(object):
|
||||
|
||||
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.
|
||||
"""
|
||||
obj = self.model(**kwargs)
|
||||
@ -425,8 +432,8 @@ class QuerySet(object):
|
||||
|
||||
def dates(self, field_name, kind, order='ASC'):
|
||||
"""
|
||||
Returns a list of datetime objects representing all available dates
|
||||
for the given field_name, scoped to 'kind'.
|
||||
Returns a list of datetime objects representing all available dates for
|
||||
the given field_name, scoped to 'kind'.
|
||||
"""
|
||||
assert kind in ("month", "year", "day"), \
|
||||
"'kind' must be one of 'year', 'month' or 'day'."
|
||||
@ -441,7 +448,7 @@ class QuerySet(object):
|
||||
|
||||
def none(self):
|
||||
"""
|
||||
Returns an empty queryset.
|
||||
Returns an empty QuerySet.
|
||||
"""
|
||||
return self._clone(klass=EmptyQuerySet)
|
||||
|
||||
@ -485,6 +492,7 @@ class QuerySet(object):
|
||||
def complex_filter(self, filter_obj):
|
||||
"""
|
||||
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()
|
||||
method) or a dictionary of keyword lookup arguments.
|
||||
|
||||
@ -500,8 +508,9 @@ class QuerySet(object):
|
||||
|
||||
def select_related(self, *fields, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance that will select related objects. If
|
||||
fields are specified, they must be ForeignKey fields and only those
|
||||
Returns a new QuerySet instance that will select related objects.
|
||||
|
||||
If fields are specified, they must be ForeignKey fields and only those
|
||||
related objects are included in the selection.
|
||||
"""
|
||||
depth = kwargs.pop('depth', 0)
|
||||
@ -521,13 +530,15 @@ class QuerySet(object):
|
||||
|
||||
def dup_select_related(self, other):
|
||||
"""
|
||||
Copies the related selection status from the queryset 'other' to the
|
||||
current queryset.
|
||||
Copies the related selection status from the QuerySet 'other' to the
|
||||
current QuerySet.
|
||||
"""
|
||||
self.query.select_related = other.query.select_related
|
||||
|
||||
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(), \
|
||||
"Cannot reorder a query once a slice has been taken."
|
||||
obj = self._clone()
|
||||
@ -544,9 +555,9 @@ class QuerySet(object):
|
||||
return obj
|
||||
|
||||
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(), \
|
||||
"Cannot change a query once a slice has been taken"
|
||||
@ -556,7 +567,7 @@ class QuerySet(object):
|
||||
|
||||
def reverse(self):
|
||||
"""
|
||||
Reverses the ordering of the queryset.
|
||||
Reverses the ordering of the QuerySet.
|
||||
"""
|
||||
clone = self._clone()
|
||||
clone.query.standard_ordering = not clone.query.standard_ordering
|
||||
@ -589,12 +600,13 @@ class QuerySet(object):
|
||||
|
||||
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
|
||||
it's useful.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ValuesQuerySet(QuerySet):
|
||||
def __init__(self, *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
|
||||
retrieving.
|
||||
|
||||
Called by the _clone() method after initialising the rest of the
|
||||
Called by the _clone() method after initializing the rest of the
|
||||
instance.
|
||||
"""
|
||||
self.extra_names = []
|
||||
@ -658,6 +670,7 @@ class ValuesQuerySet(QuerySet):
|
||||
raise TypeError("Merging '%s' classes must involve the same values in each case."
|
||||
% self.__class__.__name__)
|
||||
|
||||
|
||||
class ValuesListQuerySet(ValuesQuerySet):
|
||||
def iterator(self):
|
||||
self.query.trim_extra_select(self.extra_names)
|
||||
@ -681,6 +694,7 @@ class ValuesListQuerySet(ValuesQuerySet):
|
||||
clone.flat = self.flat
|
||||
return clone
|
||||
|
||||
|
||||
class DateQuerySet(QuerySet):
|
||||
def iterator(self):
|
||||
return self.query.results_iter()
|
||||
@ -689,7 +703,7 @@ class DateQuerySet(QuerySet):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
self.query = self.query.clone(klass=sql.DateQuery, setup=True)
|
||||
@ -706,6 +720,7 @@ class DateQuerySet(QuerySet):
|
||||
c._setup_query()
|
||||
return c
|
||||
|
||||
|
||||
class EmptyQuerySet(QuerySet):
|
||||
def __init__(self, model=None, query=None):
|
||||
super(EmptyQuerySet, self).__init__(model, query)
|
||||
@ -733,6 +748,7 @@ class EmptyQuerySet(QuerySet):
|
||||
# (it raises StopIteration immediately).
|
||||
yield iter([]).next()
|
||||
|
||||
|
||||
# QOperator, QNot, QAnd and QOr are temporarily retained for backwards
|
||||
# compatibility. All the old functionality is now part of the 'Q' class.
|
||||
class QOperator(Q):
|
||||
@ -743,12 +759,14 @@ class QOperator(Q):
|
||||
|
||||
QOr = QAnd = QOperator
|
||||
|
||||
|
||||
def QNot(q):
|
||||
warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2)
|
||||
return ~q
|
||||
|
||||
|
||||
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
|
||||
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)
|
||||
return obj, index_end
|
||||
|
||||
|
||||
def delete_objects(seen_objs):
|
||||
"""
|
||||
Iterate through a list of seen classes, and remove any instances that are
|
||||
@ -782,10 +801,10 @@ def delete_objects(seen_objs):
|
||||
try:
|
||||
ordered_classes = seen_objs.keys()
|
||||
except CyclicDependency:
|
||||
# if there is a cyclic dependency, we cannot in general delete
|
||||
# the objects. However, if an appropriate transaction is set
|
||||
# up, or if the database is lax enough, it will succeed.
|
||||
# So for now, we go ahead and try anway.
|
||||
# If there is a cyclic dependency, we cannot in general delete the
|
||||
# objects. However, if an appropriate transaction is set up, or if the
|
||||
# database is lax enough, it will succeed. So for now, we go ahead and
|
||||
# try anyway.
|
||||
ordered_classes = seen_objs.unordered_keys()
|
||||
|
||||
obj_pairs = {}
|
||||
@ -794,7 +813,7 @@ def delete_objects(seen_objs):
|
||||
items.sort()
|
||||
obj_pairs[cls] = items
|
||||
|
||||
# Pre notify all instances to be deleted
|
||||
# Pre-notify all instances to be deleted.
|
||||
for pk_val, instance in items:
|
||||
dispatcher.send(signal=signals.pre_delete, sender=cls,
|
||||
instance=instance)
|
||||
@ -808,7 +827,7 @@ def delete_objects(seen_objs):
|
||||
if field.rel and field.null and field.rel.to in seen_objs:
|
||||
update_query.clear_related(field, pk_list)
|
||||
|
||||
# Now delete the actual data
|
||||
# Now delete the actual data.
|
||||
for cls in ordered_classes:
|
||||
items = obj_pairs[cls]
|
||||
items.reverse()
|
||||
@ -831,6 +850,7 @@ def delete_objects(seen_objs):
|
||||
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
|
||||
def insert_query(model, values, return_id=False, raw_values=False):
|
||||
"""
|
||||
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.insert_values(values, raw_values)
|
||||
return query.execute_sql(return_id)
|
||||
|
||||
|
@ -136,6 +136,7 @@ class MultiPartParser(object):
|
||||
# since we cannot be sure a file is complete until
|
||||
# we hit the next boundary/part of the multipart content.
|
||||
self.handle_file_complete(old_field_name, counters)
|
||||
old_field_name = None
|
||||
|
||||
try:
|
||||
disposition = meta_data['content-disposition'][1]
|
||||
|
@ -31,3 +31,54 @@ def conditional_content_removal(request, response):
|
||||
if request.method == 'HEAD':
|
||||
response.content = ''
|
||||
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
|
||||
|
||||
|
@ -27,7 +27,7 @@ from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
|
||||
|
||||
from util import ErrorList, ValidationError
|
||||
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
|
||||
|
||||
__all__ = (
|
||||
'Field', 'CharField', 'IntegerField',
|
||||
@ -419,18 +419,6 @@ except ImportError:
|
||||
# It's OK if Django settings aren't configured.
|
||||
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):
|
||||
widget = FileInput
|
||||
@ -460,23 +448,20 @@ class FileField(Field):
|
||||
category = DeprecationWarning,
|
||||
stacklevel = 2
|
||||
)
|
||||
data = UploadedFile(data['filename'], data['content'])
|
||||
|
||||
try:
|
||||
file_name = data.file_name
|
||||
file_size = data.file_size
|
||||
file_name = data.name
|
||||
file_size = data.size
|
||||
except AttributeError:
|
||||
try:
|
||||
file_name = data.get('filename')
|
||||
file_size = bool(data['content'])
|
||||
except (AttributeError, KeyError):
|
||||
raise ValidationError(self.error_messages['invalid'])
|
||||
raise ValidationError(self.error_messages['invalid'])
|
||||
|
||||
if not file_name:
|
||||
raise ValidationError(self.error_messages['invalid'])
|
||||
if not file_size:
|
||||
raise ValidationError(self.error_messages['empty'])
|
||||
|
||||
return UploadedFile(file_name, data)
|
||||
return data
|
||||
|
||||
class ImageField(FileField):
|
||||
default_error_messages = {
|
||||
|
@ -686,7 +686,7 @@ class FileUploadField(FormField):
|
||||
if upload_errors:
|
||||
raise validators.CriticalValidationError, upload_errors
|
||||
try:
|
||||
file_size = new_data.file_size
|
||||
file_size = new_data.size
|
||||
except AttributeError:
|
||||
file_size = len(new_data['content'])
|
||||
if not file_size:
|
||||
|
@ -90,32 +90,34 @@ def encode_multipart(boundary, data):
|
||||
"""
|
||||
lines = []
|
||||
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():
|
||||
if isinstance(value, file):
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
'Content-Disposition: form-data; name="%s"; filename="%s"' \
|
||||
% (to_str(key), to_str(os.path.basename(value.name))),
|
||||
'Content-Type: application/octet-stream',
|
||||
'',
|
||||
value.read()
|
||||
])
|
||||
else:
|
||||
if not isinstance(value, basestring) and is_iterable(value):
|
||||
for item in value:
|
||||
if is_file(value):
|
||||
lines.extend(encode_file(boundary, key, value))
|
||||
elif not isinstance(value, basestring) and is_iterable(value):
|
||||
for item in value:
|
||||
if is_file(item):
|
||||
lines.extend(encode_file(boundary, key, item))
|
||||
else:
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % to_str(key),
|
||||
'',
|
||||
to_str(item)
|
||||
])
|
||||
else:
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % to_str(key),
|
||||
'',
|
||||
to_str(value)
|
||||
])
|
||||
else:
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % to_str(key),
|
||||
'',
|
||||
to_str(value)
|
||||
])
|
||||
|
||||
lines.extend([
|
||||
'--' + boundary + '--',
|
||||
@ -123,6 +125,17 @@ def encode_multipart(boundary, data):
|
||||
])
|
||||
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:
|
||||
"""
|
||||
A class that can act as a client for testing purposes.
|
||||
|
@ -161,6 +161,15 @@ def translation(language):
|
||||
|
||||
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):
|
||||
t = _translation(path)
|
||||
if t is not None:
|
||||
|
@ -450,11 +450,11 @@ TECHNICAL_500_TEMPLATE = """
|
||||
{% if frame.context_line %}
|
||||
<div class="context" id="c{{ frame.id }}">
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
<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 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.template import loader, RequestContext
|
||||
from django.http import Http404, HttpResponse
|
||||
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
|
||||
|
||||
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 = {}
|
||||
queryset = queryset._clone()
|
||||
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:
|
||||
page = request.GET.get('page', 1)
|
||||
try:
|
||||
|
@ -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
|
||||
represented as page ``1``.
|
||||
|
||||
An example of the use of pagination can be found in the `object pagination`_
|
||||
example model.
|
||||
For more on pagination, read the `pagination documentation`_.
|
||||
|
||||
.. _`object pagination`: ../models/pagination/
|
||||
.. _`pagination documentation`: ../pagination/
|
||||
|
||||
**New in Django development version:**
|
||||
|
||||
As a special case, you are also permitted to use
|
||||
``last`` as a value for ``page``::
|
||||
As a special case, you are also permitted to use ``last`` as a value for
|
||||
``page``::
|
||||
|
||||
/objects/?page=last
|
||||
|
||||
|
@ -1334,23 +1334,12 @@ given length.
|
||||
* Validates that non-empty file data has been bound to the form.
|
||||
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
|
||||
|
||||
An ``UploadedFile`` object has two attributes:
|
||||
|
||||
====================== ====================================================
|
||||
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.
|
||||
To learn more about the ``UploadedFile`` object, see the `file uploads documentation`_.
|
||||
|
||||
When you use a ``FileField`` in a form, you must also remember to
|
||||
`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`_
|
||||
|
||||
``FilePathField``
|
||||
|
@ -59,6 +59,11 @@ page::
|
||||
...
|
||||
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
|
||||
=====================
|
||||
|
||||
@ -77,6 +82,21 @@ Attributes
|
||||
|
||||
``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
|
||||
================
|
||||
|
||||
@ -116,13 +136,6 @@ Attributes
|
||||
|
||||
``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
|
||||
====================================
|
||||
|
||||
|
@ -279,7 +279,7 @@ Default: ``''`` (Empty string)
|
||||
|
||||
The database backend to use. The build-in database backends are
|
||||
``'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
|
||||
ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e.
|
||||
|
@ -64,7 +64,7 @@ methods to access the uploaded content:
|
||||
``UploadedFile.read()``
|
||||
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
|
||||
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.
|
||||
|
||||
``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::
|
||||
|
||||
def handle_uploaded_file(f):
|
||||
destination = open('some/file/name.txt', 'wb')
|
||||
destination = open('some/file/name.txt', 'wb+')
|
||||
for chunk in f.chunks():
|
||||
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
|
||||
``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
|
||||
given, chunks will be 64 kb.
|
||||
given, chunks will be 64 KB.
|
||||
|
||||
``UploadedFile.multiple_chunks(self, chunk_size=None)``
|
||||
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``
|
||||
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
|
||||
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()``
|
||||
Only files uploaded onto disk will have this method; it returns the full
|
||||
path to the temporary uploaded file.
|
||||
|
||||
|
||||
Upload Handlers
|
||||
===============
|
||||
|
||||
|
@ -803,7 +803,7 @@ False
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> type(f.cleaned_data['file'])
|
||||
<class 'django.newforms.fields.UploadedFile'>
|
||||
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||
>>> instance = f.save()
|
||||
>>> instance.file
|
||||
u'...test1.txt'
|
||||
@ -814,7 +814,7 @@ u'...test1.txt'
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> type(f.cleaned_data['file'])
|
||||
<class 'django.newforms.fields.UploadedFile'>
|
||||
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||
>>> instance = f.save()
|
||||
>>> instance.file
|
||||
u'...test1.txt'
|
||||
@ -906,7 +906,7 @@ u'...test3.txt'
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> type(f.cleaned_data['image'])
|
||||
<class 'django.newforms.fields.UploadedFile'>
|
||||
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||
>>> instance = f.save()
|
||||
>>> instance.image
|
||||
u'...test.png'
|
||||
@ -918,7 +918,7 @@ u'...test.png'
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> type(f.cleaned_data['image'])
|
||||
<class 'django.newforms.fields.UploadedFile'>
|
||||
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||
>>> instance = f.save()
|
||||
>>> instance.image
|
||||
u'...test.png'
|
||||
|
@ -31,7 +31,7 @@ __test__ = {'API_TESTS':"""
|
||||
# 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.count
|
||||
9
|
||||
@ -82,15 +82,15 @@ True
|
||||
>>> p.end_index()
|
||||
9
|
||||
|
||||
# Invalid pages raise InvalidPage.
|
||||
# Empty pages raise EmptyPage.
|
||||
>>> paginator.page(0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvalidPage: ...
|
||||
EmptyPage: ...
|
||||
>>> paginator.page(3)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvalidPage: ...
|
||||
EmptyPage: ...
|
||||
|
||||
# Empty paginators with 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
|
||||
>>> filterwarnings("ignore")
|
||||
|
||||
>>> from django.core.paginator import ObjectPaginator, InvalidPage
|
||||
>>> from django.core.paginator import ObjectPaginator, EmptyPage
|
||||
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
|
||||
>>> paginator.hits
|
||||
9
|
||||
@ -181,15 +181,15 @@ True
|
||||
>>> paginator.last_on_page(1)
|
||||
9
|
||||
|
||||
# Invalid pages raise InvalidPage.
|
||||
# Invalid pages raise EmptyPage.
|
||||
>>> paginator.get_page(-1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvalidPage: ...
|
||||
EmptyPage: ...
|
||||
>>> paginator.get_page(2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvalidPage: ...
|
||||
EmptyPage: ...
|
||||
|
||||
# Empty paginators with allow_empty_first_page=True.
|
||||
>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)
|
||||
|
0
tests/regressiontests/admin_scripts/__init__.py
Normal file
0
tests/regressiontests/admin_scripts/__init__.py
Normal 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()))
|
||||
|
@ -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()))
|
@ -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()))
|
@ -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())
|
12
tests/regressiontests/admin_scripts/models.py
Normal file
12
tests/regressiontests/admin_scripts/models.py
Normal 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')
|
||||
|
817
tests/regressiontests/admin_scripts/tests.py
Normal file
817
tests/regressiontests/admin_scripts/tests.py
Normal 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)]")
|
@ -156,3 +156,26 @@ class FileUploadTests(TestCase):
|
||||
{'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)
|
||||
|
@ -7,4 +7,5 @@ urlpatterns = patterns('',
|
||||
(r'^echo/$', views.file_upload_echo),
|
||||
(r'^quota/$', views.file_upload_quota),
|
||||
(r'^quota/broken/$', views.file_upload_quota_broken),
|
||||
(r'^getlist_count/$', views.file_upload_getlist_count),
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ def file_upload_view(request):
|
||||
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,
|
||||
# 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 HttpResponse('')
|
||||
else:
|
||||
@ -29,7 +29,7 @@ def file_upload_view_verify(request):
|
||||
form_data.update(request.FILES)
|
||||
|
||||
# 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()
|
||||
|
||||
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.
|
||||
"""
|
||||
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))
|
||||
|
||||
def file_upload_quota(request):
|
||||
@ -68,3 +68,13 @@ def file_upload_quota_broken(request):
|
||||
response = file_upload_echo(request)
|
||||
request.upload_handlers.insert(0, QuotaUploadHandler())
|
||||
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))
|
||||
|
@ -800,10 +800,10 @@ Traceback (most recent call last):
|
||||
ValidationError: [u'The submitted file is empty.']
|
||||
|
||||
>>> 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'))
|
||||
<class 'django.newforms.fields.UploadedFile'>
|
||||
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||
|
||||
# URLField ##################################################################
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user