Permalink
Browse files

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
  • Loading branch information...
brosner committed Jul 10, 2008
1 parent 9cbd07b commit 60c060c4762f55418da5628bbe6525e7820e9c61
Showing with 1,341 additions and 306 deletions.
  1. +1 −0 AUTHORS
  2. +2 −3 django/contrib/admin/views/main.py
  3. +114 −43 django/core/files/uploadedfile.py
  4. +6 −37 django/core/files/uploadhandler.py
  5. +6 −2 django/core/handlers/base.py
  6. +2 −0 django/core/mail.py
  7. +17 −15 django/core/paginator.py
  8. +35 −34 django/db/models/base.py
  9. +8 −5 django/db/models/fields/__init__.py
  10. +70 −51 django/db/models/query.py
  11. +1 −0 django/http/multipartparser.py
  12. +51 −0 django/http/utils.py
  13. +6 −21 django/newforms/fields.py
  14. +1 −1 django/oldforms/__init__.py
  15. +32 −19 django/test/client.py
  16. +9 −0 django/utils/translation/trans_real.py
  17. +2 −2 django/views/debug.py
  18. +2 −2 django/views/generic/list_detail.py
  19. +4 −5 docs/generic_views.txt
  20. +2 −13 docs/newforms.txt
  21. +20 −7 docs/pagination.txt
  22. +1 −1 docs/settings.txt
  23. +27 −23 docs/upload_handling.txt
  24. +4 −4 tests/modeltests/model_forms/models.py
  25. +8 −8 tests/modeltests/pagination/models.py
  26. 0 tests/regressiontests/admin_scripts/__init__.py
  27. 0 tests/regressiontests/admin_scripts/management/__init__.py
  28. 0 tests/regressiontests/admin_scripts/management/commands/__init__.py
  29. +10 −0 tests/regressiontests/admin_scripts/management/commands/app_command.py
  30. +9 −0 tests/regressiontests/admin_scripts/management/commands/base_command.py
  31. +9 −0 tests/regressiontests/admin_scripts/management/commands/label_command.py
  32. +9 −0 tests/regressiontests/admin_scripts/management/commands/noargs_command.py
  33. +12 −0 tests/regressiontests/admin_scripts/models.py
  34. +817 −0 tests/regressiontests/admin_scripts/tests.py
  35. +27 −4 tests/regressiontests/file_uploads/tests.py
  36. +1 −0 tests/regressiontests/file_uploads/urls.py
  37. +14 −4 tests/regressiontests/file_uploads/views.py
  38. +2 −2 tests/regressiontests/forms/fields.py
View
@@ -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 @@ def get_query_string(self, new_params=None, remove=None):
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 @@
"""
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,34 +48,34 @@ 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 _get_name(self):
+ return self._name
- def _set_file_name(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.
name = os.path.basename(name)
-
+
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
name = name[:255 - len(ext)] + ext
-
- self._file_name = name
-
- def _get_file_name(self):
- return self._file_name
-
- file_name = property(_get_file_name, _set_file_name)
- def chunk(self, chunk_size=None):
+ self._name = name
+
+ name = property(_get_name, _set_name)
+
+ 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 @@ def chunk(self, chunk_size=None):
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 @@ def multiple_chunks(self, chunk_size=None):
"""
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 @@ def open(self):
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
-
- 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)
+ return self.name
+
+ # 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 @@ def open(self):
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 @@ def new_file(self, file_name, *args, **kwargs):
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 @@ def file_complete(self, file_size):
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 @@
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
View
@@ -205,10 +205,12 @@ def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
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 = []
Oops, something went wrong.

0 comments on commit 60c060c

Please sign in to comment.