Ticket #2070: 2070_revision7388.diff
File 2070_revision7388.diff, 98.6 KB (added by , 17 years ago) |
---|
-
django/http/multipartparser.py
1 """ 2 MultiPart parsing for file uploads. 3 4 This object will take the file upload headers 5 and the file upload handler and chunk the upload 6 data for the handler to deal with. 7 """ 8 from django.utils.datastructures import MultiValueDict 9 from django.utils.encoding import force_unicode 10 from django.utils.text import unescape_entities 11 12 __all__ = ('MultiPartParser','MultiPartParserError','InputStreamExhausted') 13 14 class MultiPartParserError(Exception): 15 pass 16 17 class InputStreamExhausted(Exception): 18 """ No more reads are allowed from this device. """ 19 pass 20 21 class FILE(object): 22 """ 23 Used to denote FILE sections of the multipart from the 24 ParseBoundaryStream. 25 26 FILE sections have a filename field, and are typically used when 27 the user is uploading a file. 28 """ 29 pass 30 31 class RAW(object): 32 """ 33 Used to denote RAW sections of the multipart from the 34 ParseBoundaryStream. 35 36 RAW is a special marker for a section that doesn't appear to have 37 headers that allow us to understand its contents. It is not a 38 standard terminology of RFC2388. 39 """ 40 pass 41 42 class FIELD(object): 43 """ 44 Used to denote RAW sections of the multipart from the 45 ParseBoundaryStream. 46 47 FIELDS are standard post fields, these are typically entries on a 48 form. 49 """ 50 pass 51 52 class MultiPartParser(object): 53 """ 54 A rfc2388 multipart/form-data parser. 55 56 parse() reads the input stream in chunk_size chunks and returns a 57 tuple of (POST MultiValueDict, FILES MultiValueDict). If 58 file_upload_dir is defined files will be streamed to temporary 59 files in the specified directory. 60 """ 61 def __init__(self, META, input_data, upload_handlers, encoding=None): 62 """ 63 Initialize the MultiPartParser object. 64 65 *META* -- The standard META dictionary in Django request objects. 66 *input_data* -- The raw post data, as a bytestring. 67 *upload_handler* -- An object of type UploadHandler 68 that performs operations on the uploaded 69 data. 70 *encoding* -- The encoding with which to treat the incoming data. 71 """ 72 # Import cgi utilities for (near) future use. 73 global valid_boundary, settings 74 from django.conf import settings 75 from cgi import valid_boundary 76 77 # 78 # Content-Type should containt multipart and the boundary information. 79 # 80 81 content_type = META.get('HTTP_CONTENT_TYPE', META.get('CONTENT_TYPE', '')) 82 if not content_type.startswith('multipart/'): 83 raise MultiPartParserError('Invalid Content-Type: %s' % 84 content_type) 85 86 # Parse the header to get the boundary to split the parts. 87 ctypes, opts = parse_header(content_type) 88 boundary = opts.get('boundary') 89 if not boundary or not valid_boundary(boundary): 90 raise MultiPartParserError('Invalid boundary in multipart: %s' % 91 boundary) 92 93 94 # 95 # Content-Length should contain the length of the body we are about 96 # to receive. 97 # 98 try: 99 content_length = int(META.get('HTTP_CONTENT_LENGTH', 100 META.get('CONTENT_LENGTH',0))) 101 except (ValueError, TypeError): 102 # For now set it to 0...we'll try again later on down. 103 content_length = 0 104 105 if content_length <= 0: 106 # This means we shouldn't continue...raise an error. 107 raise MultiPartParserError("Invalid content length: %r" % content_length) 108 109 self._boundary = boundary 110 self._input_data = input_data 111 112 # For compatibility with low-level network APIs (with 32-bit integers), 113 # the chunk size should be < 2^31, but still divisible by 4. 114 self._chunk_size = min(2147483644, *[x.chunk_size for x in upload_handlers 115 if x.chunk_size]) 116 117 self._meta = META 118 self._encoding = encoding or settings.DEFAULT_CHARSET 119 self._content_length = content_length 120 self._upload_handlers = upload_handlers 121 122 def parse(self): 123 """ 124 Parse the POST data and break it into a FILES MultiValueDict 125 and a POST MultiValueDict. 126 127 *returns* -- A tuple containing the POST and FILES dictionary, 128 respectively. 129 """ 130 from django.core.files.fileuploadhandler import StopUpload, SkipFile 131 from django.http import QueryDict 132 133 encoding = self._encoding 134 handlers = self._upload_handlers 135 136 limited_input_data = LimitBytes(self._input_data, self._content_length) 137 138 # See if the handler will want to take care of the parsing. 139 # This allows overriding everything if somebody wants it. 140 for handler in handlers: 141 result = handler.handle_raw_input(limited_input_data, 142 self._meta, 143 self._content_length, 144 self._boundary, 145 encoding) 146 if result is not None: 147 return result[0], result[1] 148 149 # Create the data structures to be used later. 150 self._post = QueryDict('', mutable=True) 151 self._files = MultiValueDict() 152 153 # Instantiate the parser and stream: 154 stream = LazyStream(ChunkIter(limited_input_data, self._chunk_size)) 155 156 # Whether or not to signal a file-completion at the beginning of the loop. 157 old_field_name = None 158 counters = [0] * len(handlers) 159 160 for item_type, meta_data, stream in Parser(stream, self._boundary): 161 if old_field_name: 162 # We run this at the beginning of the next loop 163 # since we cannot be sure a file is complete until 164 # we hit the next boundary/part of the multipart content. 165 self.handle_file_complete(old_field_name, counters) 166 167 try: 168 disposition = meta_data['content-disposition'][1] 169 field_name = disposition['name'].strip() 170 except (KeyError, IndexError, AttributeError): 171 continue 172 173 transfer_encoding = meta_data.get('content-transfer-encoding') 174 field_name = force_unicode(field_name, encoding, errors='replace') 175 176 if item_type is FIELD: 177 # This is a post field, we can just set it in the post 178 if transfer_encoding == 'base64': 179 raw_data = stream.read() 180 try: 181 data = str(raw_data).decode('base64') 182 except: 183 data = raw_data 184 else: 185 data = stream.read() 186 187 self._post.appendlist(field_name, 188 force_unicode(data, encoding, errors='replace')) 189 elif item_type is FILE: 190 # This is a file, use the handler... 191 file_successful = True 192 file_name = disposition.get('filename') 193 if not file_name: 194 continue 195 file_name = force_unicode(file_name, encoding, errors='replace') 196 file_name = self.IE_sanitize(unescape_entities(file_name)) 197 198 199 content_type = meta_data.get('content-type', ('',))[0].strip() 200 try: 201 charset = meta_data.get('content-type', (0,{}))[1].get('charset', None) 202 except: 203 charset = None 204 205 try: 206 content_length = int(meta_data.get('content-length')[0]) 207 except (IndexError, TypeError, ValueError): 208 content_length = None 209 210 counters = [0] * len(handlers) 211 try: 212 for handler in handlers: 213 retval = handler.new_file(field_name, file_name, 214 content_type, content_length, 215 charset) 216 if retval: 217 break 218 219 for chunk in stream: 220 if transfer_encoding == 'base64': 221 # We only special-case base64 transfer encoding 222 try: 223 chunk = str(chunk).decode('base64') 224 except Exception, e: 225 # Since this is only a chunk, any error is an unfixable error. 226 raise MultiValueParseError("Could not decode base64 data: %r" % e) 227 228 for i, handler in enumerate(handlers): 229 chunk_length = len(chunk) 230 chunk = handler.receive_data_chunk(chunk, 231 counters[i]) 232 counters[i] += chunk_length 233 if chunk is None: 234 # If the chunk received by the handler is None, then don't continue. 235 break 236 237 except (StopUpload, SkipFile), e: 238 file_successful = False 239 if isinstance(e, SkipFile): 240 # Just use up the rest of this file... 241 exhaust(stream) 242 elif isinstance(e, StopUpload): 243 # Abort the parsing and break 244 parser.abort() 245 break 246 else: 247 # Handle file upload completions on next iteration. 248 old_field_name = field_name 249 else: 250 # If this is neither a FIELD or a FILE, just exhaust the stream. 251 exhaust(stream) 252 253 # Make sure that the request data is all fed 254 exhaust(limited_input_data) 255 # Signal that the upload has completed. 256 for handler in handlers: 257 retval = handler.upload_complete() 258 if retval: 259 break 260 261 return self._post, self._files 262 263 def handle_file_complete(self, old_field_name, counters): 264 """ 265 Handle all the signalling that takes place when a file is complete. 266 """ 267 for i, handler in enumerate(self._upload_handlers): 268 file_obj = handler.file_complete(counters[i]) 269 if file_obj: 270 # If it returns a file object, then set the files dict. 271 self._files.appendlist(force_unicode(old_field_name, 272 self._encoding, 273 errors='replace'), 274 file_obj) 275 break 276 277 def IE_sanitize(self, filename): 278 """cleanup filename from IE full paths""" 279 return filename and filename[filename.rfind("\\")+1:].strip() 280 281 282 class LazyStream(object): 283 """ 284 The LazyStream wrapper allows one to pull and "unget" bytes from a stream. 285 286 Given a producer object (an iterator that yields bytestrings), the 287 LazyStream object will support iteration, reading, and keeping a 288 "look-back" variable in case you need to "unget" some bytes. 289 """ 290 def __init__(self, producer, length=None): 291 """ 292 Every LazyStream must have a producer when instantiated. 293 294 A producer is an iterable that returns a string each time it 295 is called. 296 """ 297 self._producer = producer 298 self._empty = False 299 self._leftover = '' 300 self.length = length 301 self._position = 0 302 self._remaining = length 303 304 # These fields are to do sanity checking to make sure we don't 305 # have infinite loops getting/ungetting from the stream. The 306 # purpose overall is to raise an exception if we perform lots 307 # of stream get/unget gymnastics without getting 308 # anywhere. Naturally this is not sound, but most probably 309 # would indicate a bug if the exception is raised. 310 311 # largest position tell us how far this lazystream has ever 312 # been advanced 313 self._largest_position = 0 314 315 # "modifications since" will start at zero and increment every 316 # time the position is modified but a new largest position is 317 # not achieved. 318 self._modifications_since = 0 319 320 def tell(self): 321 return self.position 322 323 def read(self, size=None): 324 def parts(): 325 remaining = (size is not None and [size] or [self._remaining])[0] 326 # do the whole thing in one shot if no limit was provided. 327 if remaining is None: 328 yield ''.join(self) 329 return 330 331 # otherwise do some bookkeeping to return exactly enough 332 # of the stream and stashing any extra content we get from 333 # the producer 334 while remaining != 0: 335 assert remaining > 0, 'remaining bytes to read should never go negative' 336 337 chunk = self.next() 338 339 emitting = chunk[:remaining] 340 self.unget(chunk[remaining:]) 341 remaining -= len(emitting) 342 yield emitting 343 344 out = ''.join(parts()) 345 return out 346 347 def next(self): 348 """ 349 Used when the exact number of bytes to read is unimportant. 350 351 This procedure just returns whatever is chunk is conveniently 352 returned from the iterator instead. Useful to avoid 353 unnecessary bookkeeping if performance is an issue. 354 """ 355 if self._leftover: 356 output = self._leftover 357 self._leftover = '' 358 else: 359 output = self._producer.next() 360 self.position += len(output) 361 return output 362 363 def close(self): 364 """ 365 Used to invalidate/disable this lazy stream. 366 367 Replaces the producer with an empty list. Any leftover bytes 368 that have already been read will still be reported upon read() 369 and/or next(). 370 """ 371 self._producer = [] 372 373 def __iter__(self): 374 return self 375 376 def unget(self, bytes): 377 """ 378 Places bytes back onto the front of the lazy stream. 379 380 Future calls to read() will return those bytes first. The 381 stream position and thus tell() will be rewound. 382 """ 383 self.position -= len(bytes) 384 self._leftover = ''.join([bytes, self._leftover]) 385 386 def _set_position(self, value): 387 if value > self._largest_position: 388 self._modifications_since = 0 389 self._largest_position = value 390 else: 391 self._modifications_since += 1 392 if self._modifications_since > 500: 393 raise MultiPartParserError("LazyStream thinks it is somehow stuck. Report this to the Django developers\n" + repr(vars(self))) 394 395 self._position = value 396 397 position = property(lambda self: self._position, _set_position) 398 399 class ChunkIter(object): 400 """ 401 An iterable that will yield chunks of data. 402 Given a file-like object as the constructor, 403 this object will yield chunks of read operations 404 from that object. 405 """ 406 def __init__(self, flo, chunk_size=64 * 1024): 407 self.flo = flo 408 self.chunk_size = chunk_size 409 410 def next(self): 411 try: 412 data = self.flo.read(self.chunk_size) 413 except InputStreamExhausted: 414 raise StopIteration 415 if data: 416 return data 417 else: 418 raise StopIteration 419 420 def __iter__(self): 421 return self 422 423 424 class LimitBytes(object): 425 """ Limit bytes for a file object. """ 426 def __init__(self, fileobject, length): 427 self._file = fileobject 428 self.remaining = length 429 430 def read(self, num_bytes=None): 431 """ 432 Read data from the underlying file. 433 If you ask for too much or there isn't anything left, 434 this will raise an InputStreamExhausted error. 435 """ 436 if self.remaining <= 0: 437 raise InputStreamExhausted() 438 if num_bytes is None: 439 num_bytes = self.remaining 440 else: 441 num_bytes = min(num_bytes, self.remaining) 442 self.remaining -= num_bytes 443 return self._file.read(num_bytes) 444 445 class InterBoundaryIter(object): 446 """ 447 A Producer that will iterate over boundaries. 448 """ 449 def __init__(self, stream, boundary): 450 self._stream = stream 451 self._boundary = boundary 452 453 def __iter__(self): 454 return self 455 456 def next(self): 457 try: 458 return LazyStream(BoundaryIter(self._stream, self._boundary)) 459 except InputStreamExhausted: 460 raise StopIteration 461 462 463 class BoundaryIter(object): 464 """ 465 A Producer that is sensitive to boundaries. 466 467 Will happily yield bytes until a boundary is found. Will yield the 468 bytes before the boundary, throw away the boundary bytes 469 themselves, and push the post-boundary bytes back on the stream. 470 471 The future calls to .next() after locating the boundary will raise 472 a StopIteration exception. 473 """ 474 475 def __init__(self, stream, boundary): 476 self._stream = stream 477 self._boundary = boundary 478 self._done = False 479 # rollback an additional six bytes because the format is like 480 # this: CRLF<boundary>[--CRLF] 481 self._rollback = len(boundary) + 6 482 483 # Try to use mx fast string search if available. Otherwise 484 # use Python find. Wrap the latter for consistency. 485 unused_char = self._stream.read(1) 486 if not unused_char: 487 raise InputStreamExhausted 488 self._stream.unget(unused_char) 489 try: 490 from mx.TextTools import FS 491 self._fs = FS(boundary).find 492 except ImportError: 493 self._fs = lambda data: data.find(boundary) 494 495 def __iter__(self): 496 return self 497 498 def next(self): 499 if self._done: 500 raise StopIteration 501 502 stream = self._stream 503 rollback = self._rollback 504 505 bytes_read = 0 506 chunks = [] 507 for bytes in stream: 508 bytes_read += len(bytes) 509 chunks.append(bytes) 510 if bytes_read > rollback: 511 break 512 if not bytes: 513 break 514 else: 515 self._done = True 516 517 if not chunks: 518 raise StopIteration 519 520 chunk = ''.join(chunks) 521 boundary = self._find_boundary(chunk, len(chunk) < self._rollback) 522 523 if boundary: 524 end, next = boundary 525 stream.unget(chunk[next:]) 526 self._done = True 527 return chunk[:end] 528 else: 529 # make sure we dont treat a partial boundary (and 530 # its separators) as data 531 if not chunk[:-rollback]:# and len(chunk) >= (len(self._boundary) + 6): 532 # There's nothing left, we should just return and mark as done. 533 self._done = True 534 return chunk 535 else: 536 stream.unget(chunk[-rollback:]) 537 return chunk[:-rollback] 538 539 def _find_boundary(self, data, eof = False): 540 """ 541 Finds a multipart boundary in data. 542 543 Should no boundry exist in the data None is returned 544 instead. Otherwise a tuple containing 545 the indices of the following are returned: 546 547 * the end of current encapsulation 548 549 * the start of the next encapsulation 550 """ 551 index = self._fs(data) 552 if index < 0: 553 return None 554 else: 555 end = index 556 next = index + len(self._boundary) 557 data_len = len(data) - 1 558 # backup over CRLF 559 if data[max(0,end-1)] == '\n': 560 end -= 1 561 if data[max(0,end-1)] == '\r': 562 end -= 1 563 # skip over --CRLF 564 #if data[min(data_len,next)] == '-': 565 # next += 1 566 #if data[min(data_len,next)] == '-': 567 # next += 1 568 #if data[min(data_len,next)] == '\r': 569 # next += 1 570 #if data[min(data_len,next)] == '\n': 571 # next += 1 572 return end, next 573 574 def exhaust(stream_or_iterable): 575 """ 576 Completely exhausts an iterator or stream. 577 578 Raise a MultiPartParserError if the argument is not a stream or an 579 iterable. 580 """ 581 iterator = None 582 try: 583 iterator = iter(stream_or_iterable) 584 except TypeError: 585 iterator = ChunkIter(stream_or_iterable, 16384) 586 587 if iterator is None: 588 raise MultiPartParserError('multipartparser.exhaust() was passed a non-iterable or stream parameter') 589 590 for __ in iterator: 591 pass 592 593 def ParseBoundaryStream(stream, max_header_size): 594 """ 595 Parses one and exactly one stream that encapsulates a boundary. 596 """ 597 # Stream at beginning of header, look for end of header 598 # and parse it if found. The header must fit within one 599 # chunk. 600 chunk = stream.read(max_header_size) 601 # 'find' returns the top of these four bytes, so we'll 602 # need to munch them later to prevent them from polluting 603 # the payload. 604 header_end = chunk.find('\r\n\r\n') 605 606 def _parse_header(line): 607 main_value_pair, params = parse_header(line) 608 try: 609 name, value = main_value_pair.split(':', 1) 610 except: 611 raise ValueError("Invalid header: %r" % line) 612 return name, (value, params) 613 614 if header_end == -1: 615 # we find no header, so we just mark this fact and pass on 616 # the stream verbatim 617 stream.unget(chunk) 618 return (RAW, {}, stream) 619 620 header = chunk[:header_end] 621 622 # here we place any excess chunk back onto the stream, as 623 # well as throwing away the CRLFCRLF bytes from above. 624 stream.unget(chunk[header_end + 4:]) 625 626 TYPE = RAW 627 outdict = {} 628 629 # Eliminate blank lines 630 for line in header.split('\r\n'): 631 # This terminology ("main value" and "dictionary of 632 # parameters") is from the Python docs. 633 try: 634 name, (value, params) = _parse_header(line) 635 except: 636 continue 637 638 if name == 'content-disposition': 639 TYPE = FIELD 640 if params.get('filename'): 641 TYPE = FILE 642 643 outdict[name] = value, params 644 645 if TYPE == RAW: 646 stream.unget(chunk) 647 648 return (TYPE, outdict, stream) 649 650 651 class Parser(object): 652 def __init__(self, stream, boundary): 653 self._stream = stream 654 self._separator = '--' + boundary 655 656 def __iter__(self): 657 658 boundarystream = InterBoundaryIter(self._stream, 659 self._separator) 660 661 for sub_stream in boundarystream: 662 # Iterate over each part 663 yield ParseBoundaryStream(sub_stream, 1024) 664 665 def parse_header(line): 666 """ Parse the header into a key-value. """ 667 plist = _parse_header_params(';' + line) 668 key = plist.pop(0).lower() 669 pdict = {} 670 for p in plist: 671 i = p.find('=') 672 if i >= 0: 673 name = p[:i].strip().lower() 674 value = p[i+1:].strip() 675 if len(value) >= 2 and value[0] == value[-1] == '"': 676 value = value[1:-1] 677 value = value.replace('\\\\', '\\').replace('\\"', '"') 678 pdict[name] = value 679 return key, pdict 680 681 def _parse_header_params(s): 682 plist = [] 683 while s[:1] == ';': 684 s = s[1:] 685 end = s.find(';') 686 while end > 0 and s.count('"', 0, end) % 2: 687 end = s.find(';', end + 1) 688 if end < 0: 689 end = len(s) 690 f = s[:end] 691 plist.append(f.strip()) 692 s = s[end:] 693 return plist -
django/http/__init__.py
9 9 except ImportError: 10 10 from cgi import parse_qsl 11 11 12 from django.utils.datastructures import MultiValueDict, FileDict12 from django.utils.datastructures import MultiValueDict, ImmutableList 13 13 from django.utils.encoding import smart_str, iri_to_uri, force_unicode 14 14 from django.http.multipartparser import MultiPartParser 15 15 from utils import * 16 16 17 17 RESERVED_CHARS="!*'();:@&=+$,/?%#[]" … … 25 25 26 26 # The encoding used in GET/POST dicts. None means use default setting. 27 27 _encoding = None 28 _upload_handlers = () 28 29 29 30 def __init__(self): 30 31 self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {} … … 102 103 103 104 encoding = property(_get_encoding, _set_encoding) 104 105 105 def parse_file_upload(header_dict, post_data): 106 """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 107 import email, email.Message 108 from cgi import parse_header 109 raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()]) 110 raw_message += '\r\n\r\n' + post_data 111 msg = email.message_from_string(raw_message) 112 POST = QueryDict('', mutable=True) 113 FILES = MultiValueDict() 114 for submessage in msg.get_payload(): 115 if submessage and isinstance(submessage, email.Message.Message): 116 name_dict = parse_header(submessage['Content-Disposition'])[1] 117 # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads 118 # or {'name': 'blah'} for POST fields 119 # We assume all uploaded files have a 'filename' set. 120 if 'filename' in name_dict: 121 assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported" 122 if not name_dict['filename'].strip(): 123 continue 124 # IE submits the full path, so trim everything but the basename. 125 # (We can't use os.path.basename because that uses the server's 126 # directory separator, which may not be the same as the 127 # client's one.) 128 filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:] 129 FILES.appendlist(name_dict['name'], FileDict({ 130 'filename': filename, 131 'content-type': 'Content-Type' in submessage and submessage['Content-Type'] or None, 132 'content': submessage.get_payload(), 133 })) 134 else: 135 POST.appendlist(name_dict['name'], submessage.get_payload()) 136 return POST, FILES 106 def _initialize_handlers(self): 107 from django.conf import settings 108 from django.core.files.fileuploadhandler import load_handler 109 handlers = [] 110 # We go through each handler in the settings variable 111 # and instantiate the handler by calling HandlerClass(request). 112 for handler in settings.FILE_UPLOAD_HANDLERS: 113 handlers.append(load_handler(handler, self)) 114 self._upload_handlers = handlers 137 115 116 def _set_upload_handlers(self, upload_handlers): 117 """ 118 Set the upload handler to the new handler given in the parameter. 119 """ 120 if hasattr(self, '_files'): 121 raise AttributeError("You cannot set the upload handlers after the upload has been processed.") 122 self._upload_handlers = upload_handlers 138 123 124 def _get_upload_handlers(self): 125 if not self._upload_handlers: 126 # If thre are no upload handlers defined, initialize them from settings. 127 self._initialize_handlers() 128 return self._upload_handlers 129 130 upload_handlers = property(_get_upload_handlers, _set_upload_handlers) 131 132 def parse_file_upload(self, META, post_data): 133 """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 134 self.upload_handlers = ImmutableList(self.upload_handlers, 135 warning="You cannot alter the upload handlers after the upload has been processed.") 136 parser = MultiPartParser(META, post_data, self.upload_handlers, 137 self.encoding) 138 return parser.parse() 139 140 139 141 class QueryDict(MultiValueDict): 140 142 """ 141 143 A specialized MultiValueDict that takes a query string when initialized. -
django/test/client.py
18 18 BOUNDARY = 'BoUnDaRyStRiNg' 19 19 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY 20 20 21 22 class FakePayload(object): 23 """ 24 A wrapper around StringIO that restricts what can be read, 25 since data from the network can't be seeked and cannot 26 be read outside of its content length (or else we hang). 27 """ 28 def __init__(self, content): 29 self.__content = StringIO(content) 30 self.__len = len(content) 31 32 def read(self, num_bytes=None): 33 if num_bytes is None: 34 num_bytes = self.__len or 1 35 assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." 36 content = self.__content.read(num_bytes) 37 self.__len -= num_bytes 38 return content 39 40 21 41 class ClientHandler(BaseHandler): 22 42 """ 23 43 A HTTP Handler that can be used for testing purposes. … … 230 250 'CONTENT_TYPE': content_type, 231 251 'PATH_INFO': urllib.unquote(path), 232 252 'REQUEST_METHOD': 'POST', 233 'wsgi.input': StringIO(post_data),253 'wsgi.input': FakePayload(post_data), 234 254 } 235 255 r.update(extra) 236 256 -
django/conf/global_settings.py
114 114 SEND_BROKEN_LINK_EMAILS = False 115 115 116 116 # Database connection info. 117 DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or ' oracle'.117 DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. 118 118 DATABASE_NAME = '' # Or path to database file if using sqlite3. 119 119 DATABASE_USER = '' # Not used with sqlite3. 120 120 DATABASE_PASSWORD = '' # Not used with sqlite3. … … 224 224 # Example: "http://media.lawrence.com" 225 225 MEDIA_URL = '' 226 226 227 # A tuple that enumerates the upload handlers 228 # in order. 229 FILE_UPLOAD_HANDLERS = ( 230 'django.core.files.fileuploadhandler.MemoryFileUploadHandler', 231 'django.core.files.fileuploadhandler.TemporaryFileUploadHandler', 232 ) 233 234 # Number of bytes the length of the request can be before it is 235 # streamed to the file system instead of parsed entirely in memory. 236 FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 237 238 # Directory to upload streamed files temporarily. 239 # A value of `None` means that it will use the default temporary 240 # directory for the server's operating system. 241 FILE_UPLOAD_TEMP_DIR = None 242 227 243 # Default formatting for date objects. See all available format strings here: 228 244 # http://www.djangoproject.com/documentation/templates/#now 229 245 DATE_FORMAT = 'N j, Y' -
django/db/models/base.py
13 13 from django.utils.datastructures import SortedDict 14 14 from django.utils.functional import curry 15 15 from django.utils.encoding import smart_str, force_unicode, smart_unicode 16 from django.core.files.filemove import file_move_safe 16 17 from django.conf import settings 17 18 from itertools import izip 19 import warnings 18 20 import types 19 21 import sys 20 22 import os … … 384 386 def _get_FIELD_size(self, field): 385 387 return os.path.getsize(self._get_FIELD_filename(field)) 386 388 387 def _save_FIELD_file(self, field, filename, raw_ contents, save=True):389 def _save_FIELD_file(self, field, filename, raw_field, save=True): 388 390 directory = field.get_directory_name() 389 391 try: # Create the date-based directory if it doesn't exist. 390 392 os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) 391 393 except OSError: # Directory probably already exists. 392 394 pass 395 396 # Put the deprecation warning first since there are multiple 397 # locations where we use the new and old interface. 398 if isinstance(raw_field, dict): 399 warnings.warn("The dictionary usage for files is deprecated. Use the new object interface instead.", DeprecationWarning) 400 401 if filename is None: 402 try: 403 filename = raw_field.file_name 404 except AttributeError: 405 filename = raw_field['filename'] 406 393 407 filename = field.get_filename(filename) 394 408 395 409 # If the filename already exists, keep adding an underscore to the name of … … 406 420 setattr(self, field.attname, filename) 407 421 408 422 full_filename = self._get_FIELD_filename(field) 409 fp = open(full_filename, 'wb') 410 fp.write(raw_contents) 411 fp.close() 423 if hasattr(raw_field, 'temporary_file_path'): 424 # This file has a file path that we can move. 425 raw_field.close() 426 file_move_safe(raw_field.temporary_file_path(), full_filename) 427 else: 428 from django.core.files import filelocks 429 fp = open(full_filename, 'wb') 430 # exclusive lock 431 filelocks.lock(fp, filelocks.LOCK_EX) 432 if hasattr(raw_field, 'chunk'): 433 # This is a normal uploadedfile that we can stream. 434 for chunk in raw_field.chunk(65535): 435 fp.write(chunk) 436 else: 437 # This is an old dictionary, use the old interface. 438 fp.write(raw_field['content']) 439 filelocks.unlock(fp) 440 fp.close() 412 441 442 413 443 # Save the width and/or height, if applicable. 414 444 if isinstance(field, ImageField) and (field.width_field or field.height_field): 415 445 from django.utils.images import get_image_dimensions -
django/db/models/fields/__init__.py
785 785 setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) 786 786 setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 787 787 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 788 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save)) 788 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 789 setattr(cls, 'move_%s_file' % self.name, lambda instance, raw_field, save=True: instance._save_FIELD_file(self, None, raw_field, save)) 789 790 dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 790 791 791 792 def delete_file(self, instance): … … 808 809 if new_data.get(upload_field_name, False): 809 810 func = getattr(new_object, 'save_%s_file' % self.name) 810 811 if rel: 811 f unc(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)812 file = new_data[upload_field_name][0] 812 813 else: 813 f unc(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)814 file = new_data[upload_field_name] 814 815 816 try: 817 file_name = file.file_name 818 except AttributeError: 819 file_name = file['filename'] 820 func(file_name, file, save) 821 815 822 def get_directory_name(self): 816 823 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to)))) 817 824 … … 823 830 def save_form_data(self, instance, data): 824 831 from django.newforms.fields import UploadedFile 825 832 if data and isinstance(data, UploadedFile): 826 getattr(instance, "save_%s_file" % self.name)(data.filename, data. content, save=False)833 getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False) 827 834 828 835 def formfield(self, **kwargs): 829 836 defaults = {'form_class': forms.FileField} -
django/oldforms/__init__.py
680 680 self.field_name, self.is_required = field_name, is_required 681 681 self.validator_list = [self.isNonEmptyFile] + validator_list 682 682 683 def isNonEmptyFile(self, field_data, all_data): 683 def isNonEmptyFile(self, new_data, all_data): 684 if hasattr(new_data, 'upload_errors'): 685 upload_errors = new_data.upload_errors() 686 if upload_errors: 687 raise validators.CriticalValidationError, upload_errors 684 688 try: 685 content = field_data['content']686 except TypeError:687 raise validators.CriticalValidationError, ugettext("No file was submitted. Check the encoding type on the form.")688 if not content:689 file_size = new_data.file_size 690 except AttributeError: 691 file_size = len(new_data['content']) 692 if not file_size: 689 693 raise validators.CriticalValidationError, ugettext("The submitted file is empty.") 690 694 691 695 def render(self, data): 692 696 return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 693 697 (self.get_id(), self.__class__.__name__, self.field_name)) 694 698 699 def prepare(self, new_data): 700 if hasattr(new_data, 'upload_errors'): 701 upload_errors = new_data.upload_errors() 702 new_data[self.field_name] = { '_file_upload_error': upload_errors } 703 695 704 def html2python(data): 696 705 if data is None: 697 706 raise EmptyValue -
django/core/handlers/wsgi.py
112 112 # Populates self._post and self._files 113 113 if self.method == 'POST': 114 114 if self.environ.get('CONTENT_TYPE', '').startswith('multipart'): 115 header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')]) 116 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '') 117 self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data) 115 self._raw_post_data = '' 116 self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) 118 117 else: 119 118 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 120 119 else: -
django/core/handlers/modpython.py
53 53 def _load_post_and_files(self): 54 54 "Populates self._post and self._files" 55 55 if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'): 56 self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data) 56 self._raw_post_data = '' 57 self._post, self._files = self.parse_file_upload(self.META, self._req) 57 58 else: 58 59 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 59 60 -
django/core/files/filelocks.py
1 """ 2 Locking portability based partially on example by 3 Jonathan Feignberg <jdf@pobox.com> in python cookbook. 4 5 Example Usage:: 6 7 from django.core.files import filelocks 8 9 f = open('./file', 'wb') 10 11 filelocks.lock(f, filelocks.LOCK_EX) 12 f.write('Django') 13 f.close() 14 """ 15 16 __all__ = ('LOCK_EX','LOCK_SH','LOCK_NB','lock','unlock') 17 18 system_type = None 19 20 try: 21 import win32con 22 import win32file 23 import pywintypes 24 LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK 25 LOCK_SH = 0 26 LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY 27 __overlapped = pywintypes.OVERLAPPED() 28 system_type = 'nt' 29 except (ImportError, AttributeError): 30 pass 31 32 try: 33 import fcntl 34 LOCK_EX = fcntl.LOCK_EX 35 LOCK_SH = fcntl.LOCK_SH 36 LOCK_NB = fcntl.LOCK_NB 37 system_type = 'posix' 38 except (ImportError, AttributeError): 39 pass 40 41 if system_type == 'nt': 42 def lock(file, flags): 43 hfile = win32file._get_osfhandle(file.fileno()) 44 win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped) 45 46 def unlock(file): 47 hfile = win32file._get_osfhandle(file.fileno()) 48 win32file.UnlockFileEx(hfile, 0, -0x10000, __overlapped) 49 elif system_type == 'posix': 50 def lock(file, flags): 51 fcntl.flock(file.fileno(), flags) 52 53 def unlock(file): 54 fcntl.flock(file.fileno(), fcntl.LOCK_UN) 55 else: 56 # File locking is not supported. 57 LOCK_EX = LOCK_SH = LOCK_NB = None 58 59 # Dummy functions that don't do anything. 60 def lock(file, flags): 61 pass 62 63 def unlock(file): 64 pass -
django/core/files/uploadedfile.py
1 """ 2 The uploaded file objects for Django. 3 This contains the base UploadedFile and the TemporaryUploadedFile 4 derived class. 5 """ 6 7 __all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile') 8 9 class UploadedFile(object): 10 """ 11 The UploadedFile object behaves somewhat like a file 12 object and represents some data that the user submitted 13 and is stored in some form. 14 """ 15 DEFAULT_CHUNK_SIZE = 64 * 2**10 16 17 def __init__(self): 18 self.file_size = None 19 self.file_name = None 20 self.content_type = None 21 self.charset = None 22 pass 23 24 def file_size(self): 25 return self.file_size 26 27 def chunk(self, chunk_size=None): 28 """ 29 Read the file to generate chunks of chunk_size bytes. 30 """ 31 if not chunk_size: 32 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 33 34 if hasattr(self, 'seek'): 35 self.seek(0) 36 # Assume the pointer is at zero... 37 counter = self.file_size() 38 39 while counter > 0: 40 yield self.read(chunk_size) 41 counter -= chunk_size 42 43 44 def multiple_chunks(self, chunk_size=None): 45 """ 46 Return True if you can expect multiple chunks, False otherwise. 47 Note: If a particular file representation is in memory, then 48 override this to return False. 49 """ 50 if not chunk_size: 51 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 52 return self.file_size() < chunk_size 53 54 55 def read(self, num_bytes=None): 56 """ 57 Read from the file in whatever representation it has. 58 """ 59 raise NotImplementedError() 60 61 def open(self): 62 """ 63 Open the file, if one needs to. 64 """ 65 pass 66 67 68 def close(self): 69 """ 70 Close the file, if one needs to. 71 """ 72 pass 73 74 def __getitem__(self, key): 75 """ 76 This maintains backwards compatibility. 77 """ 78 import warnings 79 warnings.warn("The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.", DeprecationWarning) 80 # Dictionary to translate labels 81 # for backwards compatbility. 82 # Should be removed at some point. 83 backwards_translate = { 84 'filename': 'file_name', 85 'content-type': 'content_type', 86 } 87 88 if key == 'content': 89 return self.read() 90 else: 91 return getattr(self, backwards_translate.get(key, key)) 92 93 def __repr__(self): 94 """ 95 This representation could be anything and can be overridden. 96 This is mostly done to make it look somewhat useful. 97 """ 98 _dict = { 99 'file_name': self.file_name, 100 'content_type': self.content_type, 101 'content': '<omitted>', 102 } 103 return repr(_dict) 104 105 106 class TemporaryUploadedFile(UploadedFile): 107 """ 108 Upload a file to a temporary file. 109 """ 110 111 def __init__(self, file, file_name, content_type, file_size, charset): 112 self.file = file 113 self.file_name = file_name 114 self.path = file.name 115 self.content_type = content_type 116 self.file_size = file_size 117 self.charset = charset 118 self.file.seek(0) 119 120 def temporary_file_path(self): 121 """ 122 Return the full path of this file. 123 """ 124 return self.path 125 126 def read(self, *args, **kwargs): 127 return self.file.read(*args, **kwargs) 128 129 def open(self): 130 """ 131 Assume the person meant to seek. 132 """ 133 self.seek(0) 134 135 def seek(self, *args, **kwargs): 136 self.file.seek(*args, **kwargs) 137 138 139 class InMemoryUploadedFile(UploadedFile): 140 """ 141 Upload a file into memory. 142 """ 143 def __init__(self, file, field_name, file_name, content_type, charset): 144 self.file = file 145 self.field_name = field_name 146 self.file_name = file_name 147 self.content_type = content_type 148 self.charset = charset 149 self.file.seek(0) 150 151 def seek(self, *args, **kwargs): 152 self.file.seek(*args, **kwargs) 153 154 def open(self): 155 self.seek(0) 156 157 def read(self, *args, **kwargs): 158 return self.file.read(*args, **kwargs) 159 160 def chunk(self, chunk_size=None): 161 """ 162 Return the entirety of the data regardless. 163 """ 164 self.file.seek(0) 165 return self.read() 166 167 def multiple_chunks(self, chunk_size=None): 168 """ 169 Since it's in memory, we'll never have multiple chunks. 170 """ 171 return False 172 173 174 class SimpleUploadedFile(InMemoryUploadedFile): 175 """ 176 A simple representation of a file, which 177 just has content, size, and a name. 178 """ 179 def __init__(self, name, content, content_type='text/plain'): 180 try: 181 from cStringIO import StringIO 182 except ImportError: 183 from StringIO import StringIO 184 self.file = StringIO(content or '') 185 self.file_name = name 186 self.field_name = None 187 self.file_size = len(content or '') 188 self.content_type = content_type 189 self.charset = None 190 self.file.seek(0) 191 -
django/core/files/__init__.py
1 -
django/core/files/fileuploadhandler.py
1 """ A fileuploadhandler base and default subclass for handling file uploads. 2 """ 3 import os 4 try: 5 from cStringIO import StringIO 6 except ImportError: 7 from StringIO import StringIO 8 9 from django.utils.encoding import force_unicode 10 from django.utils.datastructures import MultiValueDict 11 from django.core.exceptions import ImproperlyConfigured 12 13 from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile 14 15 __all__ = ('UploadFileException','StopUpload', 'SkipFile', 16 'FileUploadHandler', 'TemporaryFileUploadHandler', 17 'MemoryFileUploadHandler', 'load_handler') 18 19 20 class UploadFileException(Exception): 21 """ Any error having to do with Uploading Files. """ 22 pass 23 24 class StopUpload(UploadFileException): 25 """ This exception is raised when an upload must abort. """ 26 pass 27 28 class SkipFile(UploadFileException): 29 """ This exception is raised when a file needs to be skipped. """ 30 pass 31 32 class FileUploadHandler(object): 33 """ FileUploadHandler will take data and handle file uploads 34 in a streamed fashion. 35 """ 36 chunk_size = 64 * 2 ** 10 #: The default chunk size is 64 KB. 37 38 def __init__(self, request=None): 39 " Initialize some local variables. " 40 self.file_name = None 41 self.content_type = None 42 self.content_length = None 43 self.charset = None 44 self.request = request 45 46 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 47 """ 48 Handle the raw input from the client. 49 Parameters: 50 *input_data* -- An object that supports reading via .read(). 51 *content_length* -- The (integer) value of the Content-Length header from the client. 52 *boundary* -- The boundary from the Content-Type header. Be sure to prepend two '--'. 53 """ 54 pass 55 56 def new_file(self, field_name, file_name, content_type, content_length, charset=None): 57 """ 58 Signal that a new file has been started. 59 60 Warning: Do not trust content_length, if you get it at all. 61 """ 62 self.field_name = field_name 63 self.file_name = file_name 64 self.content_type = content_type 65 self.content_length = content_length 66 self.charset = charset 67 68 def receive_data_chunk(self, raw_data, start): 69 """ 70 Receive data from the streamed upload parser. 71 Start is the position in the file of the chunk. 72 """ 73 raise NotImplementedError() 74 75 def file_complete(self, file_size): 76 """ 77 Signal that a file has completed. 78 File size corresponds to the actual size accumulated 79 by all the chunks. 80 81 This should return a valid UploadedFile object. 82 """ 83 raise NotImplementedError() 84 85 def upload_complete(self): 86 """ 87 Signal that the upload is complete. 88 Do any cleanup that is necessary for this handler. 89 """ 90 pass 91 92 93 94 class TemporaryFileUploadHandler(FileUploadHandler): 95 """ 96 Upload the streaming data into a temporary file. 97 """ 98 def __init__(self, *args, **kwargs): 99 """ Import settings for later. """ 100 super(TemporaryFileUploadHandler, self).__init__(*args, **kwargs) 101 global settings 102 from django.conf import settings 103 104 def new_file(self, file_name, *args, **kwargs): 105 """ 106 Create the file object to append to as data is coming in. 107 """ 108 super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs) 109 self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR) 110 self.write = self.file.write 111 112 def receive_data_chunk(self, raw_data, start): 113 """ 114 Once we get the data, we will save it to our file. 115 """ 116 self.write(raw_data) 117 118 def file_complete(self, file_size): 119 """ 120 Signal that a file has completed. 121 File size corresponds to the actual size accumulated 122 by all the chunks. 123 124 This should return a valid UploadedFile object. 125 """ 126 self.file.seek(0) 127 return TemporaryUploadedFile(self.file, self.file_name, 128 self.content_type, file_size, 129 self.charset) 130 131 132 class MemoryFileUploadHandler(FileUploadHandler): 133 """ 134 The MemoryFileUploadHandler will place the data directly into memory. 135 """ 136 137 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 138 """ 139 Use the content_length to signal whether or not this handler should be in use. 140 """ 141 global settings 142 from django.conf import settings 143 144 # If the the post is too large, we cannot use the Memory handler. 145 if content_length > settings.FILE_UPLOAD_MAX_MEMORY_SIZE: 146 self.activated = False 147 else: 148 self.activated = True 149 150 def new_file(self, *args, **kwargs): 151 super(MemoryFileUploadHandler, self).new_file(*args, **kwargs) 152 if self.activated: 153 self.file = StringIO() 154 return "Stop" 155 156 def receive_data_chunk(self, raw_data, start): 157 """ 158 Add the data to the StringIO file. 159 """ 160 if self.activated: 161 self.file.write(raw_data) 162 else: 163 return raw_data 164 165 def file_complete(self, file_size): 166 """ 167 Return a file object if we're activated. 168 """ 169 if not self.activated: 170 return 171 172 return InMemoryUploadedFile(self.file, self.field_name, self.file_name, 173 self.content_type, self.charset) 174 175 176 class TemporaryFile(object): 177 """ 178 A temporary file that tries to delete itself when garbage collected. 179 """ 180 def __init__(self, dir): 181 import tempfile 182 if not dir: 183 dir = tempfile.gettempdir() 184 try: 185 (fd, name) = tempfile.mkstemp(suffix='.upload', dir=dir) 186 self.file = os.fdopen(fd, 'w+b') 187 except (OSError, IOError): 188 raise OSError, "Could not create temporary file for uploading, have you set settings.FILE_UPLOAD_TEMP_DIR correctly?" 189 self.name = name 190 191 def __getattr__(self, name): 192 a = getattr(self.__dict__['file'], name) 193 if type(a) != type(0): 194 setattr(self, name, a) 195 return a 196 197 def __del__(self): 198 try: 199 os.unlink(self.name) 200 except OSError: 201 pass 202 203 def load_handler(path, request, *args, **kwargs): 204 """ 205 Given a path to a handler return the instantiation of that handler. 206 E.g.:: 207 load_handler('django.core.files.fileuploadhandler.TemporaryFileUploadHandler', request) 208 will return a TemporaryFileUploadHandler object. 209 """ 210 request = RestrictedRequest(request) 211 i = path.rfind('.') 212 module, attr = path[:i], path[i+1:] 213 try: 214 mod = __import__(module, {}, {}, [attr]) 215 except ImportError, e: 216 raise ImproperlyConfigured, 'Error importing upload handler module %s: "%s"' % (module, e) 217 except ValueError, e: 218 raise ImproperlyConfigured, 'Error importing upload handler module Is FILE_UPLOAD_HANDLERS a correctly defined list or tuple?' 219 try: 220 cls = getattr(mod, attr) 221 except AttributeError: 222 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" upload handler backend' % (module, attr) 223 return cls(request, *args, **kwargs) 224 225 class RestrictedRequest(object): 226 """ 227 Wraps a request object but prevents the POST and FILES 228 attribute from being accessed. 229 Example:: 230 request = RestrictedRequest(request) 231 request.POST -> AttributeError 232 233 The restricted request can safetly be given to upload 234 handlers and don't have to worry about infinite recursion. 235 """ 236 def __init__(self, request): 237 self.__request = request 238 239 def __getattr__(self, name): 240 if name in ('POST', 'FILES'): 241 raise AttributeError, "You cannot access POST or FILES in upload handlers." 242 elif name == 'RestrictedRequest__request': 243 return super(RestrictedRequest, self).__getattr__(name) 244 else: 245 return getattr(self.__request, name) -
django/core/files/filemove.py
1 import os 2 3 __all__ = ('file_move_safe',) 4 5 try: 6 import shutil 7 file_move = shutil.move 8 except ImportError: 9 file_move = os.rename 10 11 def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_overwrite=False): 12 """ 13 Moves a file from one location to another in the safest way possible. 14 15 First, it tries using shutils.move, which is OS-dependent but doesn't 16 break with change of filesystems. Then it tries os.rename, which will 17 break if it encounters a change in filesystems. Lastly, it streams 18 it manually from one file to another in python. 19 20 Without ``allow_overwrite``, if the destination file exists, the 21 file will raise an IOError. 22 """ 23 24 from django.core.files import filelocks 25 26 if old_file_name == new_file_name: 27 # No file moving takes place. 28 return 29 30 if not allow_overwrite and os.path.exists(new_file_name): 31 raise IOError, "Django does not allow overwriting files." 32 33 try: 34 file_move(old_file_name, new_file_name) 35 return 36 except OSError: # moving to another filesystem 37 pass 38 39 new_file = open(new_file_name, 'wb') 40 # exclusive lock 41 filelocks.lock(new_file, filelocks.LOCK_EX) 42 old_file = open(old_file_name, 'rb') 43 current_chunk = None 44 45 while current_chunk != '': 46 current_chunk = old_file.read(chunk_size) 47 new_file.write(current_chunk) 48 49 new_file.close() 50 old_file.close() 51 52 os.remove(old_file_name) -
django/utils/datastructures.py
332 332 except TypeError: # Special-case if current isn't a dict. 333 333 current = {bits[-1]: v} 334 334 335 class FileDict(dict):335 class ImmutableList(tuple): 336 336 """ 337 A dictionary used to hold uploaded file contents. The only special feature 338 here is that repr() of this object won't dump the entire contents of the 339 file to the output. A handy safeguard for a large file upload. 337 A tuple-like object that raises useful 338 errors when it is asked to mutate. 339 Example:: 340 a = ImmutableList(range(5), warning=AttributeError("You cannot mutate this.")) 341 a[3] = '4' 342 (Raises the AttributeError) 340 343 """ 341 def __repr__(self): 342 if 'content' in self: 343 d = dict(self, content='<omitted>') 344 return dict.__repr__(d) 345 return dict.__repr__(self) 344 345 def __new__(cls, *args, **kwargs): 346 if 'warning' in kwargs: 347 warning = kwargs['warning'] 348 del kwargs['warning'] 349 else: 350 warning = 'ImmutableList object is immutable.' 351 self = tuple.__new__(cls, *args, **kwargs) 352 self.warning = warning 353 return self 354 355 def complain(self, *wargs, **kwargs): 356 if isinstance(self.warning, Exception): 357 raise self.warning 358 else: 359 raise AttributeError, self.warning 360 361 # All list mutation functions become complain. 362 __delitem__ = __delslice__ = __iadd__ = __imul__ = complain 363 __setitem__ = __setslice__ = complain 364 append = extend = insert = pop = remove = complain 365 sort = reverse = complain -
django/utils/text.py
3 3 from django.utils.encoding import force_unicode 4 4 from django.utils.functional import allow_lazy 5 5 from django.utils.translation import ugettext_lazy 6 from htmlentitydefs import name2codepoint 6 7 7 8 # Capitalizes the first letter of a string. 8 9 capfirst = lambda x: x and force_unicode(x)[0].upper() + force_unicode(x)[1:] … … 218 219 yield bit 219 220 smart_split = allow_lazy(smart_split, unicode) 220 221 222 def _replace_entity(match): 223 text = match.group(1) 224 if text[0] == u'#': 225 text = text[1:] 226 try: 227 if text[0] in u'xX': 228 c = int(text[1:], 16) 229 else: 230 c = int(text) 231 return unichr(c) 232 except ValueError: 233 return match.group(0) 234 else: 235 try: 236 return unichr(name2codepoint[text]) 237 except (ValueError, KeyError): 238 return match.group(0) 239 240 _entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));") 241 242 def unescape_entities(text): 243 return _entity_re.sub(_replace_entity, text) 244 unescape_entities = allow_lazy(unescape_entities, unicode) -
django/newforms/fields.py
4 4 5 5 import copy 6 6 import datetime 7 import warnings 7 8 import os 8 9 import re 9 10 import time … … 416 417 417 418 class UploadedFile(StrAndUnicode): 418 419 "A wrapper for files uploaded in a FileField" 419 def __init__(self, filename, content):420 def __init__(self, filename, data): 420 421 self.filename = filename 421 self. content = content422 self.data = data 422 423 423 424 def __unicode__(self): 424 425 """ … … 444 445 return None 445 446 elif not data and initial: 446 447 return initial 448 449 if isinstance(data, dict): 450 # We warn once, then support both ways below. 451 warnings.warn("The dictionary usage for files is deprecated. Use the new object interface instead.", DeprecationWarning) 452 447 453 try: 448 f = UploadedFile(data['filename'], data['content']) 449 except TypeError: 454 file_name = data.file_name 455 file_size = data.file_size 456 except AttributeError: 457 try: 458 file_name = data.get('filename') 459 file_size = bool(data['content']) 460 except (AttributeError, KeyError): 461 raise ValidationError(self.error_messages['invalid']) 462 463 if not file_name: 450 464 raise ValidationError(self.error_messages['invalid']) 451 except KeyError: 452 raise ValidationError(self.error_messages['missing']) 453 if not f.content: 465 if not file_size: 454 466 raise ValidationError(self.error_messages['empty']) 455 return f456 467 468 return UploadedFile(file_name, data) 469 457 470 class ImageField(FileField): 458 471 default_error_messages = { 459 472 'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), … … 470 483 elif not data and initial: 471 484 return initial 472 485 from PIL import Image 473 from cStringIO import StringIO 486 487 # We need to get the file, it either has a path 488 # or we have to read it all into memory... 489 if hasattr(data, 'temporary_file_path'): 490 file = data.temporary_file_path() 491 else: 492 try: 493 from cStringIO import StringIO 494 except ImportError: 495 from StringIO import StringIO 496 if hasattr(data, 'read'): 497 file = StringIO(data.read()) 498 else: 499 file = StringIO(data['content']) 500 474 501 try: 475 502 # load() is the only method that can spot a truncated JPEG, 476 503 # but it cannot be called sanely after verify() 477 trial_image = Image.open( StringIO(f.content))504 trial_image = Image.open(file) 478 505 trial_image.load() 506 507 # Since we're about to use the file again, we have to 508 # reset the cursor of the file object if it has a cursor 509 # to reset. 510 if hasattr(file, 'reset'): 511 file.reset() 512 479 513 # verify() is the only method that can spot a corrupt PNG, 480 514 # but it must be called immediately after the constructor 481 trial_image = Image.open( StringIO(f.content))515 trial_image = Image.open(file) 482 516 trial_image.verify() 483 517 except Exception: # Python Imaging Library doesn't recognize it as an image 484 518 raise ValidationError(self.error_messages['invalid_image']) -
tests/modeltests/model_forms/models.py
67 67 68 68 class ImageFile(models.Model): 69 69 description = models.CharField(max_length=20) 70 image = models.FileField(upload_to=tempfile.gettempdir()) 70 try: 71 # If PIL is available, try testing PIL. 72 # Otherwise, it's equivalent to TextFile above. 73 import Image 74 image = models.ImageField(upload_to=tempfile.gettempdir()) 75 except ImportError: 76 image = models.FileField(upload_to=tempfile.gettempdir()) 71 77 72 78 def __unicode__(self): 73 79 return self.description … … 75 81 __test__ = {'API_TESTS': """ 76 82 >>> from django import newforms as forms 77 83 >>> from django.newforms.models import ModelForm 84 >>> from django.core.files.uploadedfile import SimpleUploadedFile 85 >>> from warnings import filterwarnings 86 >>> filterwarnings("ignore") 78 87 79 88 The bare bones, absolutely nothing custom, basic case. 80 89 … … 792 801 793 802 # Upload a file and ensure it all works as expected. 794 803 804 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 805 >>> f.is_valid() 806 True 807 >>> type(f.cleaned_data['file']) 808 <class 'django.newforms.fields.UploadedFile'> 809 >>> instance = f.save() 810 >>> instance.file 811 u'...test1.txt' 812 813 >>> os.unlink(instance.get_file_filename()) 814 795 815 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test1.txt', 'content': 'hello world'}}) 796 816 >>> f.is_valid() 797 817 True … … 814 834 u'...test1.txt' 815 835 816 836 # Delete the current file since this is not done by Django. 817 818 837 >>> os.unlink(instance.get_file_filename()) 819 838 820 839 # Override the file by uploading a new one. 821 840 822 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}, instance=instance)841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance) 823 842 >>> f.is_valid() 824 843 True 825 844 >>> instance = f.save() 826 845 >>> instance.file 827 846 u'...test2.txt' 828 847 848 # Delete the current file since this is not done by Django. 849 >>> os.unlink(instance.get_file_filename()) 850 851 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}) 852 >>> f.is_valid() 853 True 854 >>> instance = f.save() 855 >>> instance.file 856 u'...test2.txt' 857 858 # Delete the current file since this is not done by Django. 859 >>> os.unlink(instance.get_file_filename()) 860 829 861 >>> instance.delete() 830 862 831 863 # Test the non-required FileField … … 838 870 >>> instance.file 839 871 '' 840 872 841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance)873 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) 842 874 >>> f.is_valid() 843 875 True 844 876 >>> instance = f.save() 845 877 >>> instance.file 846 878 u'...test3.txt' 879 880 # Delete the current file since this is not done by Django. 881 >>> os.unlink(instance.get_file_filename()) 847 882 >>> instance.delete() 848 883 884 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}) 885 >>> f.is_valid() 886 True 887 >>> instance = f.save() 888 >>> instance.file 889 u'...test3.txt' 890 891 # Delete the current file since this is not done by Django. 892 >>> os.unlink(instance.get_file_filename()) 893 >>> instance.delete() 894 849 895 # ImageField ################################################################### 850 896 851 897 # ImageField and FileField are nearly identical, but they differ slighty when … … 858 904 859 905 >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read() 860 906 907 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) 908 >>> f.is_valid() 909 True 910 >>> type(f.cleaned_data['image']) 911 <class 'django.newforms.fields.UploadedFile'> 912 >>> instance = f.save() 913 >>> instance.image 914 u'...test.png' 915 916 # Delete the current file since this is not done by Django. 917 >>> os.unlink(instance.get_image_filename()) 918 861 919 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': {'filename': 'test.png', 'content': image_data}}) 862 920 >>> f.is_valid() 863 921 True … … 885 943 886 944 # Override the file by uploading a new one. 887 945 888 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}, instance=instance)946 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance) 889 947 >>> f.is_valid() 890 948 True 891 949 >>> instance = f.save() 892 950 >>> instance.image 893 951 u'...test2.png' 894 952 953 # Delete the current file since this is not done by Django. 954 >>> os.unlink(instance.get_image_filename()) 895 955 >>> instance.delete() 896 956 957 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}) 958 >>> f.is_valid() 959 True 960 >>> instance = f.save() 961 >>> instance.image 962 u'...test2.png' 963 964 # Delete the current file since this is not done by Django. 965 >>> os.unlink(instance.get_image_filename()) 966 >>> instance.delete() 967 897 968 # Test the non-required ImageField 898 969 899 970 >>> f = ImageFileForm(data={'description': u'Test'}) … … 904 975 >>> instance.image 905 976 '' 906 977 907 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance)978 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) 908 979 >>> f.is_valid() 909 980 True 910 981 >>> instance = f.save() 911 982 >>> instance.image 912 983 u'...test3.png' 984 985 # Delete the current file since this is not done by Django. 986 >>> os.unlink(instance.get_image_filename()) 913 987 >>> instance.delete() 914 988 989 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}) 990 >>> f.is_valid() 991 True 992 >>> instance = f.save() 993 >>> instance.image 994 u'...test3.png' 995 >>> instance.delete() 996 915 997 """} -
tests/regressiontests/bug639/tests.py
9 9 from regressiontests.bug639.models import Photo 10 10 from django.http import QueryDict 11 11 from django.utils.datastructures import MultiValueDict 12 from django.core.files.uploadedfile import SimpleUploadedFile 12 13 13 14 class Bug639Test(unittest.TestCase): 14 15 … … 21 22 22 23 # Fake a request query dict with the file 23 24 qd = QueryDict("title=Testing&image=", mutable=True) 24 qd["image_file"] = { 25 "filename" : "test.jpg", 26 "content-type" : "image/jpeg", 27 "content" : img 28 } 29 25 #qd["image_file"] = { 26 # "filename" : "test.jpg", 27 # "content-type" : "image/jpeg", 28 # "content" : img 29 # } 30 qd["image_file"] = SimpleUploadedFile('test.jpg', img, 'image/jpeg') 31 30 32 manip = Photo.AddManipulator() 31 33 manip.do_html2python(qd) 32 34 p = manip.save(qd) … … 39 41 Make sure to delete the "uploaded" file to avoid clogging /tmp. 40 42 """ 41 43 p = Photo.objects.get() 42 os.unlink(p.get_image_filename()) 43 No newline at end of file 44 os.unlink(p.get_image_filename()) -
tests/regressiontests/forms/error_messages.py
1 1 # -*- coding: utf-8 -*- 2 2 tests = r""" 3 3 >>> from django.newforms import * 4 >>> from django.core.files.uploadedfile import SimpleUploadedFile 4 5 5 6 # CharField ################################################################### 6 7 … … 214 215 Traceback (most recent call last): 215 216 ... 216 217 ValidationError: [u'INVALID'] 217 >>> f.clean( {})218 >>> f.clean(SimpleUploadedFile('name', None)) 218 219 Traceback (most recent call last): 219 220 ... 220 ValidationError: [u' MISSING']221 >>> f.clean( {'filename': 'name', 'content':''})221 ValidationError: [u'EMPTY FILE'] 222 >>> f.clean(SimpleUploadedFile('name', '')) 222 223 Traceback (most recent call last): 223 224 ... 224 225 ValidationError: [u'EMPTY FILE'] -
tests/regressiontests/forms/tests.py
26 26 from regressions import tests as regression_tests 27 27 from util import tests as util_tests 28 28 from widgets import tests as widgets_tests 29 from warnings import filterwarnings 30 filterwarnings("ignore") 29 31 30 32 __test__ = { 31 33 'extra_tests': extra_tests, -
tests/regressiontests/forms/fields.py
2 2 tests = r""" 3 3 >>> from django.newforms import * 4 4 >>> from django.newforms.widgets import RadioFieldRenderer 5 >>> from django.core.files.uploadedfile import SimpleUploadedFile 5 6 >>> import datetime 6 7 >>> import time 7 8 >>> import re … … 773 774 >>> f.clean({}) 774 775 Traceback (most recent call last): 775 776 ... 776 ValidationError: [u'No file was submitted. ']777 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 777 778 778 779 >>> f.clean({}, '') 779 780 Traceback (most recent call last): 780 781 ... 781 ValidationError: [u'No file was submitted. ']782 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 782 783 783 784 >>> f.clean({}, 'files/test3.pdf') 784 785 'files/test3.pdf' … … 788 789 ... 789 790 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 790 791 791 >>> f.clean( {'filename': 'name', 'content': None})792 >>> f.clean(SimpleUploadedFile('name', None)) 792 793 Traceback (most recent call last): 793 794 ... 794 795 ValidationError: [u'The submitted file is empty.'] 795 796 796 >>> f.clean( {'filename': 'name', 'content': ''})797 >>> f.clean(SimpleUploadedFile('name', '')) 797 798 Traceback (most recent call last): 798 799 ... 799 800 ValidationError: [u'The submitted file is empty.'] 800 801 801 >>> type(f.clean( {'filename': 'name', 'content': 'Some File Content'}))802 >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'))) 802 803 <class 'django.newforms.fields.UploadedFile'> 803 804 804 >>> type(f.clean( {'filename': 'name', 'content': 'Some File Content'}, 'files/test4.pdf'))805 >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf')) 805 806 <class 'django.newforms.fields.UploadedFile'> 806 807 807 808 # URLField ################################################################## -
tests/regressiontests/forms/forms.py
1 1 # -*- coding: utf-8 -*- 2 2 tests = r""" 3 3 >>> from django.newforms import * 4 >>> from django.core.files.uploadedfile import SimpleUploadedFile 4 5 >>> import datetime 5 6 >>> import time 6 7 >>> import re … … 1465 1466 >>> print f 1466 1467 <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> 1467 1468 1468 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False)1469 >>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', '')}, auto_id=False) 1469 1470 >>> print f 1470 1471 <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> 1471 1472 … … 1473 1474 >>> print f 1474 1475 <tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> 1475 1476 1476 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False)1477 >>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', 'some content')}, auto_id=False) 1477 1478 >>> print f 1478 1479 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 1479 1480 >>> f.is_valid() -
tests/regressiontests/test_client_regress/views.py
1 1 from django.contrib.auth.decorators import login_required 2 2 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError 3 import sha 3 4 4 5 def no_template_view(request): 5 6 "A simple view that expects a GET request, and returns a rendered template" … … 10 11 Check that a file upload can be updated into the POST dictionary without 11 12 going pear-shaped. 12 13 """ 14 from django.core.files.uploadedfile import UploadedFile 13 15 form_data = request.POST.copy() 14 16 form_data.update(request.FILES) 15 if isinstance(form_data ['file_field'], dict) and isinstance(form_data['name'], unicode):17 if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode): 16 18 return HttpResponse('') 17 19 else: 18 20 return HttpResponseServerError() 19 21 22 def file_upload_view_verify(request): 23 """ 24 Use the sha digest hash to verify the uploaded contents. 25 """ 26 from django.core.files.uploadedfile import UploadedFile 27 form_data = request.POST.copy() 28 form_data.update(request.FILES) 29 30 # Check to see if unicode names worked out. 31 if not request.FILES['file_unicode'].file_name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'): 32 return HttpResponseServerError() 33 34 for key, value in form_data.items(): 35 if key.endswith('_hash'): 36 continue 37 if key + '_hash' not in form_data: 38 continue 39 submitted_hash = form_data[key + '_hash'] 40 if isinstance(value, UploadedFile): 41 new_hash = sha.new(value.read()).hexdigest() 42 else: 43 new_hash = sha.new(value).hexdigest() 44 if new_hash != submitted_hash: 45 return HttpResponseServerError() 46 47 return HttpResponse('') 48 20 49 def get_view(request): 21 50 "A simple login protected view" 22 51 return HttpResponse("Hello world") … … 37 66 def login_protected_redirect_view(request): 38 67 "A view that redirects all requests to the GET view" 39 68 return HttpResponseRedirect('/test_client_regress/get_view/') 40 login_protected_redirect_view = login_required(login_protected_redirect_view) 41 No newline at end of file 69 login_protected_redirect_view = login_required(login_protected_redirect_view) -
tests/regressiontests/test_client_regress/models.py
5 5 from django.test import Client, TestCase 6 6 from django.core.urlresolvers import reverse 7 7 import os 8 import sha 8 9 9 10 class AssertContainsTests(TestCase): 10 11 def test_contains(self): … … 243 244 response = self.client.post('/test_client_regress/file_upload/', post_data) 244 245 self.assertEqual(response.status_code, 200) 245 246 247 def test_large_upload(self): 248 import tempfile 249 dir = tempfile.gettempdir() 250 251 (fd, name1) = tempfile.mkstemp(suffix='.file1', dir=dir) 252 file1 = os.fdopen(fd, 'w+b') 253 file1.write('a' * (2 ** 21)) 254 file1.seek(0) 255 256 (fd, name2) = tempfile.mkstemp(suffix='.file2', dir=dir) 257 file2 = os.fdopen(fd, 'w+b') 258 file2.write('a' * (10 * 2 ** 20)) 259 file2.seek(0) 260 261 # This file contains chinese symbols for a name. 262 name3 = os.path.join(dir, u'test_中文_Orl\u00e9ans.jpg') 263 file3 = open(name3, 'w+b') 264 file3.write('b' * (2 ** 10)) 265 file3.seek(0) 266 267 post_data = { 268 'name': 'Ringo', 269 'file_field1': file1, 270 'file_field2': file2, 271 'file_unicode': file3, 272 } 273 274 for key in post_data.keys(): 275 try: 276 post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest() 277 post_data[key].seek(0) 278 except AttributeError: 279 post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest() 280 281 response = self.client.post('/test_client_regress/file_upload_verify/', post_data) 282 283 for name in (name1, name2, name3): 284 try: 285 os.unlink(name) 286 except: 287 pass 288 289 self.assertEqual(response.status_code, 200) 290 291 246 292 class LoginTests(TestCase): 247 293 fixtures = ['testdata'] 248 294 -
tests/regressiontests/test_client_regress/urls.py
4 4 urlpatterns = patterns('', 5 5 (r'^no_template_view/$', views.no_template_view), 6 6 (r'^file_upload/$', views.file_upload_view), 7 (r'^file_upload_verify/$', views.file_upload_view_verify), 7 8 (r'^get_view/$', views.get_view), 8 9 url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'), 9 10 (r'^login_protected_redirect_view/$', views.login_protected_redirect_view) -
tests/regressiontests/datastructures/tests.py
117 117 >>> d['person']['2']['firstname'] 118 118 ['Adrian'] 119 119 120 ### FileDict ################################################################ 121 122 >>> d = FileDict({'content': 'once upon a time...'}) 120 ### ImmutableList ################################################################ 121 >>> d = ImmutableList(range(10)) 122 >>> d.sort() 123 Traceback (most recent call last): 124 File "<stdin>", line 1, in <module> 125 File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain 126 raise AttributeError, self.warning 127 AttributeError: ImmutableList object is immutable. 123 128 >>> repr(d) 124 "{'content': '<omitted>'}" 125 >>> d = FileDict({'other-key': 'once upon a time...'}) 126 >>> repr(d) 127 "{'other-key': 'once upon a time...'}" 129 '(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)' 130 >>> d = ImmutableList(range(10), warning="Object is immutable!") 131 >>> d[1] 132 1 133 >>> d[1] = 'test' 134 Traceback (most recent call last): 135 File "<stdin>", line 1, in <module> 136 File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain 137 raise AttributeError, self.warning 138 AttributeError: Object is immutable! 128 139 """ -
AUTHORS
58 58 Jökull Sólberg Auðunsson <jokullsolberg@gmail.com> 59 59 Arthur <avandorp@gmail.com> 60 60 David Avsajanishvili <avsd05@gmail.com> 61 axiak@mit.edu61 Mike Axiak <axiak@mit.edu> 62 62 Niran Babalola <niran@niran.org> 63 63 Morten Bagai <m@bagai.com> 64 64 Mikaël Barbero <mikael.barbero nospam at nospam free.fr> … … 135 135 Marc Fargas <telenieko@telenieko.com> 136 136 Szilveszter Farkas <szilveszter.farkas@gmail.com> 137 137 favo@exoweb.net 138 fdr <drfarina@gmail.com> 138 139 Dmitri Fedortchenko <zeraien@gmail.com> 139 140 Bill Fenner <fenner@gmail.com> 140 141 Stefane Fermgier <sf@fermigier.com> -
docs/upload_handling.txt
1 ============ 2 File Uploads 3 ============ 4 5 **New in Django development version** 6 7 Most Web sites wouldn't be complete without a way to upload files. Before the 8 file even gets into ``request.FILES`` Django has to decide where to put the 9 all the incoming data. This document describes how Django takes the incoming 10 request and populates ``request.FILES``. 11 12 .. _Request and response objects: ../request_response/#attributes 13 14 Default Behavior 15 ================ 16 17 After setting up a quick model that contains a `FileField`_ and enabling the 18 `Admin interface`_ you can quickly use a form to upload files to your site. 19 By default, if the upload is smaller than **2.5 Megabytes** in size, Django 20 will hold the entire contents of the upload in memory, and the file will 21 quickly be saved to its final destination (as defined by 22 `settings.MEDIA_ROOT`_) without any intermediary. 23 24 If the entire upload is larger than 2.5 Megabytes, then it will -- by 25 default -- write the contents of the uploaded file to a temporary file 26 in your operating system's default temporary directory. On a posix platform, 27 this means that you could expect Django to generate a file similar in name 28 to ``/tmp/tmpzfp6I6.upload``. During the upload, you may notice this file 29 grow in size as Django loads the data onto disk. 30 31 .. note:: 32 You may find that the default temporary directory is not writable. 33 This is especially true if you are on shared hosting. If this is 34 the case, Django will raise an error when you try to upload. Please 35 read below in `Extending the Default`_ to change this directory. 36 37 38 .. _FileField: ../model-api/#filefield 39 .. _Admin interface: ../tutorial02/#activate-the-admin-site 40 .. _settings.MEDIA_ROOT: ../settings/#media-root 41 42 Extending the Default 43 ===================== 44 45 Suppose you have quite a bit of memory. You may decide that you want Django 46 to stream only if the entire upload exceeds 10 Megabytes. You may even 47 decide that whatever directory is the platform default is not suitable 48 for your project. If this is the case, Django provides these two settings: 49 50 =============================== ====================================== 51 Setting Description 52 =============================== ====================================== 53 ``FILE_UPLOAD_MAX_MEMORY_SIZE`` The maximum size of a request in bytes 54 for which Django will try to load the 55 entire upload contents in memory. 56 57 ``FILE_UPLOAD_TEMP_DIR`` The directory on the file system where 58 uploaded contents will be temporarily 59 stored if not completely in memory. 60 E.g.: ``"/tmp"`` 61 =============================== ====================================== 62 63 There is one final setting -- ``FILE_UPLOAD_HANDLERS`` -- which allows complete 64 customization of the upload process. 65 66 Upload Handlers 67 =============== 68 69 Through upload handlers Django provides the flexibility to extend the 70 upload process beyond simple storage. You can use custom handlers to enforce 71 user-level quotas, compress data on the fly, render progress bars, and 72 even send data to another warehouse directly without storing it locally. 73 74 There are two pieces to the Django upload handling: the upload handler and the 75 uploaded file. These are both represented by python classes -- 76 ``FileUploadHandler`` and ``UploadedFile`` respectively. Throughout the lifetime 77 of the upload process, Django will call on the upload handler to handle the 78 upload, while the upload handler is expected to provide Django an ``UploadedFile`` 79 object to successfully complete a file's upload. 80 81 Setting Default Upload Handlers for your Project 82 ------------------------------------------------ 83 84 Similar to `Middleware`_, upload handlers have an order that is initially 85 defined in ``settings.FILE_UPLOAD_HANDLERS``. The default value is:: 86 87 ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", 88 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) 89 90 This literally means: Try putting the upload in memory first and failing 91 that put the upload in a temporary file. 92 93 This behavior, however, is completely dependent on how each of those handlers 94 is written. For example, if someone gave you a ``Rot13UploadHandler``, you can 95 update your ``settings`` to contain:: 96 97 FILE_UPLOAD_HANDLERS = ( 98 "app.uploadhandlers.rot13.Rot13UploadHandler", 99 "django.core.files.fileuploadhandler.MemoryFileUploadHandler", 100 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler", 101 ) 102 103 And the ``Rot13UploadHandler`` will perform the ``rot13`` operation on all 104 data before the data get to subsequent handlers. 105 106 .. _Middleware: ../middleware/ 107 108 Modifying your Upload Handlers Dynamically 109 ------------------------------------------ 110 111 During the lifetime of your project, you may realize that a particular 112 view or views require different uploading behavior. For this reason, 113 the ``request`` object contains a list of handlers 114 (``request.upload_handlers``) that will be called in order. To append 115 a handler to the list, you would append to it like any other list. 116 For example, suppose you had an ``ProgressBarUploadHandler`` class. 117 To append it to your upload handlers you would write:: 118 119 request.upload_handlers.append(ProgressBarUploadHandler()) 120 121 However, since the progress bar handler would probably need to run before the 122 other handlers get a chance, you'd probably want to insert it. That is, you'd 123 write:: 124 125 request.upload_handlers.insert(0, ProgressBarUploadHandler()) 126 127 If you want to replace the upload handlers completely, you can just assign a new 128 list:: 129 130 request.upload_handlers = [ProgressBarUploadHandler()] 131 132 And all Django will do is keep a progress, but do nothing with the file itself! 133 One more note: After the upload has completed, you are no longer allowed to 134 assign or modify this list, and Django will raise an error if you try to modify 135 this list after an upload. 136 137 Writing a File Upload Handler 138 ----------------------------- 139 140 All file upload handlers are subclasses of ``FileUploadHandler``, found in 141 ``django.core.files.fileuploadhandler``. To create your handler, you need to 142 define the required methods followed by the methods that make most sense to you: 143 144 chunk_size 145 ~~~~~~~~~~ 146 147 An integer attribute that specifies what sized chunks Django should store 148 into memory and feed into the handler. The chunk sizes should be divisible by 149 ``4`` and should not exceed ``2 ** 31 - 1`` in size. When there are multiple 150 chunk sizes provided by multiple handlers, Django will use the smallest chunk 151 size. 152 153 __init__ 154 ~~~~~~~~ 155 156 The constructor, which should optionally support taking a request object as its 157 first argument. When called via ``settings.FILE_UPLOAD_HANDLERS``, it will be 158 passed a request object. 159 160 Interface: ``__init__(self, request=None)`` 161 162 ``request`` is the request object. Do not access ``request.POST`` or 163 ``request.FILES``. 164 165 new_file 166 ~~~~~~~~ 167 168 ``new_file`` signals that a new file is starting. You can initialize a file or 169 whatever else is needed on a new file upload. 170 171 Interface: ``new_file(self, field_name, file_name, content_type, content_length, 172 charset)`` 173 174 ``field_name`` is a string name of the field this was POSTed as. 175 176 ``file_name`` is the unicode filename that was provided by the browser. 177 178 ``content_type`` is the MIME type provided by the browser -- E.g. 179 ``'image/jpeg'``. 180 181 ``content_length`` is the length of the image given by the browser if provided, 182 ``None`` otherwise. 183 184 ``charset`` is the charset given by the browser if provided, ``None`` otherwise. 185 186 Returns: ``None`` if you want other handlers to get ``new_file`` called. 187 Something nonzero if you don't want subsequent handlers to get a chance. 188 189 receive_data_chunk 190 ~~~~~~~~~~~~~~~~~~ 191 *required* 192 193 Receives a segment of data from the file upload. For example: The 194 ``TemporaryFileUploadHandler`` takes the data and writes it to disk. 195 196 Interface: ``receive_data_chunk(self, raw_data, start)`` 197 198 ``raw_data`` is a byte string containing the uploaded data. 199 200 ``start`` is the position in the file where this ``raw_data`` chunk begins. 201 202 Returns: ``None`` if you don't want the subsequent upload handlers to receive 203 the data. Whatever else you return gets fed into the subsequent upload handlers' 204 ``receive_data_chunk`` method. In this way, one handler can be a "filter" for 205 other handlers. 206 207 Exceptions: If you raise a ``StopUpload`` or a ``SkipFile`` exception, the 208 upload will abort or the file will be skipped respectively. 209 210 file_complete 211 ~~~~~~~~~~~~~ 212 *required* 213 214 Signals that a file has finished uploading. Expected to return an 215 ``UploadedFile`` object if the file has been packaged successfully. 216 217 Interface: ``file_complete(self, file_size)`` 218 219 ``file_size`` is the number of bytes you have received for this file. 220 221 Returns: An ``UploadedFile`` object to set in the ``request.FILES`` dictionary. 222 ``None`` if you want the subsequent upload handlers to get an ``UploadedFile`` 223 object. 224 225 upload_complete 226 ~~~~~~~~~~~~~~~ 227 228 Defines when the entire upload has completed. 229 230 Interface: ``upload_complete(self)`` 231 232 handle_raw_input 233 ~~~~~~~~~~~~~~~~ 234 235 Allows the handler to completely override the parsing of the raw HTTP-layered 236 input. 237 238 Interface: ``handle_raw_input(self, input_data, META, content_length, boundary, 239 encoding)`` 240 241 ``input_data`` is a file-like object that supports the read operation. 242 243 ``META`` is the same object as ``request.META``. 244 245 ``content_length`` is the length of the data in ``input_data``. Don't read more 246 than ``content_length`` bytes from ``input_data``. 247 248 ``boundary`` is the MIME boundary for this request. 249 250 ``encoding`` is the encoding of the request. 251 252 Returns: ``None`` if you want to go on to the next stage, ``(POST, FILES)`` if 253 you want to return the new data structures suitable for the request directly. 254 255 Defining an Uploaded File 256 ------------------------- 257 258 All file upload handlers are subclasses of ``UploadedFile``, found in 259 ``django.core.files.uploadedfile``. The uploaded file object is returned by the 260 handler above in ``file_complete``. To create your own uploaded file class, you 261 need to define the required methods followed by the methods that make most sense 262 to you: 263 264 read 265 ~~~~ 266 *required* 267 268 Interface: ``read(self, num_bytes=None)`` 269 270 Returns: A byte string of length ``num_bytes`` (or the size of the file if 271 ``num_bytes`` is not supplied). 272 273 chunk 274 ~~~~~ 275 276 A generator to yield small chunks from the file. With the ``read()`` defined, 277 the ``UploadedFile`` class already defines a ``chunk()`` method that's probably 278 suitable. 279 280 Interface: ``chunk(self, chunk_size=None)`` 281 282 multiple_chunks 283 ~~~~~~~~~~~~~~~ 284 285 Interface: ``multiple_chunks(self, chunk_size=None)`` 286 287 Returns: ``True`` or ``False`` depending on whether or not the user of this 288 file can expect more than one chunk when calling ``chunk(self, chunk_size)``. 289 If all the data is in memory, you should return ``False``. 290 291 temporary_file_path 292 ~~~~~~~~~~~~~~~~~~~ 293 294 If defined, this method should return the file path of the file on the 295 operating system. This will let users move files rather than write to 296 new files if the option is available. 297 298 Interface: ``temporary_file_path(self)`` 299 300 Returns: A file path in a file system on the local computer. 301 302 upload_errors 303 ~~~~~~~~~~~~~ 304 305 If defined, this method should return a string describing errors that 306 occured during uploads. 307 308 Interface: ``upload_errors(self)`` 309 310 Returns: A ``unicode`` string describing an error that occured with the 311 file's upload. -
docs/db-api.txt
1306 1306 Using raw strings (e.g., ``r'foo'`` instead of ``'foo'``) for passing in the 1307 1307 regular expression syntax is recommended. 1308 1308 1309 Regular expression matching is not supported on the ``ado_mssql`` backend. 1310 It will raise a ``NotImplementedError`` at runtime. 1311 1309 1312 iregex 1310 1313 ~~~~~~ 1311 1314 -
docs/settings.txt
279 279 280 280 The database backend to use. The build-in database backends are 281 281 ``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, 282 ``'sqlite3'`` and ``'oracle'``.282 ``'sqlite3'``, ``'oracle'``, or ``'ado_mssql'``. 283 283 284 284 In the Django development version, you can use a database backend that doesn't 285 285 ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e. … … 513 513 The character encoding used to decode any files read from disk. This includes 514 514 template files and initial SQL data files. 515 515 516 FILE_UPLOAD_HANDLERS 517 -------------------- 518 519 **New in Django development version** 520 521 Default:: 522 523 ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", 524 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) 525 526 A tuple of handlers to use for uploading. 527 528 FILE_UPLOAD_MAX_MEMORY_SIZE 529 --------------------------- 530 531 **New in Django development version** 532 533 Default: ``2621440`` 534 535 The maximum size (in bytes) that an upload will be before it gets streamed to the file system. 536 537 FILE_UPLOAD_TEMP_DIR 538 -------------------- 539 540 **New in Django development version** 541 542 Default: ``None`` 543 544 The directory to store data temporarily while uploading files. If ``None``, Django will use the standard temporary directory for the operating system. For example, this will default to '/tmp' on *nix-style operating systems. 545 516 546 FIXTURE_DIRS 517 547 ------------- 518 548 -
docs/newforms.txt
805 805 need to bind the file data containing the mugshot image:: 806 806 807 807 # Bound form with an image field 808 >>> from django.core.files.uploadedfile import SimpleUploadedFile 808 809 >>> data = {'subject': 'hello', 809 810 ... 'message': 'Hi there', 810 811 ... 'sender': 'foo@example.com', 811 812 ... 'cc_myself': True} 812 >>> file_data = {'mugshot': {'filename':'face.jpg' 813 ... 'content': <file data>}} 813 >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)} 814 814 >>> f = ContactFormWithMugshot(data, file_data) 815 815 816 816 In practice, you will usually specify ``request.FILES`` as the source