Commits

Anonymous committed 77a4027

[multi-db] Merge trunk to [3812]. Some tests still failing.

Comments (0)

Files changed (37)

 people who have submitted patches, reported bugs, added translations, helped
 answer newbie questions, and generally made Django that much better:
 
+    adurdin@gmail.com
     akaihola
     Andreas
     ant9000@netwise.it
     Alex Dedul
     deric@monowerks.com
     dne@mayonnaise.net
+    Maximillian Dornseif <md@hudora.de>
+    dummy@habmalnefrage.de
     Jeremy Dunck <http://dunck.us/>
     Andy Dustman <farcepest@gmail.com>
     Clint Ecker
+    favo@exoweb.net
     gandalf@owca.info
     Baishampayan Ghose
     martin.glueck@gmail.com
     Simon Greenhill <dev@simon.net.nz>
     Espen Grindhaug <http://grindhaug.org/>
     Brant Harris
+    heckj@mac.com
     hipertracker@gmail.com
     Ian Holsman <http://feh.holsman.net/>
     Kieran Holland <http://www.kieranholland.com>
     lakin.wecker@gmail.com
     Stuart Langridge <http://www.kryogenix.org/>
     Eugene Lazutkin <http://lazutkin.com/blog/>
+    Jeong-Min Lee
     Christopher Lenz <http://www.cmlenz.net/>
     limodou
     Martin Maney <http://www.chipy.org/Martin_Maney>
     Daniel Poelzleithner <http://poelzi.org/>
     J. Rademaker
     Michael Radziej <mir@noris.de>
+    ramiro
     Brian Ray <http://brianray.chipy.org/>
     rhettg@gmail.com
     Oliver Rutherfurd <http://rutherfurd.net/>
 To get more help:
 
     * Join the #django channel on irc.freenode.net. Lots of helpful people
-      hang out there. Read the archives at http://loglibrary.com/179 .
+      hang out there. Read the archives at http://simon.bofh.ms/logger/django/ .
 
     * Join the django-users mailing list, or read the archives, at
-      http://groups-beta.google.com/group/django-users.
+      http://groups.google.com/group/django-users.
 
 To contribute to Django:
 

django/conf/global_settings.py

 
 COMMENTS_ALLOW_PROFANITIES = False
 
+# The profanities that will trigger a validation error in the
+# 'hasNoProfanities' validator. All of these should be in lower-case.
+PROFANITIES_LIST = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit']
+
 # The group ID that designates which users are banned.
 # Set to None if you're not using it.
 COMMENTS_BANNED_USERS_GROUP = None

django/contrib/admin/templatetags/admin_modify.py

         context.push()
         if relation.field.rel.edit_inline == models.TABULAR:
             bound_related_object_class = TabularBoundRelatedObject
+        elif relation.field.rel.edit_inline == models.STACKED:
+            bound_related_object_class = StackedBoundRelatedObject
         else:
-            bound_related_object_class = StackedBoundRelatedObject
+            bound_related_object_class = relation.field.rel.edit_inline
         original = context.get('original', None)
         bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
         context['bound_related_object'] = bound_related_object

django/contrib/admin/views/main.py

             for bit in self.query.split():
                 or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
                 other_qs = QuerySet(self.model)
+                if qs._select_related:
+                    other_qs = other_qs.select_related()
                 other_qs = other_qs.filter(reduce(operator.or_, or_queries))
                 qs = qs & other_qs
 

django/contrib/auth/decorators.py

     to the log-in page if necessary.
     """
     )
+
+def permission_required(perm, login_url=LOGIN_URL):
+    """
+    Decorator for views that checks if a user has a particular permission
+    enabled, redirectiing to the log-in page if necessary.
+    """
+    return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
+

django/contrib/auth/handlers/modpython.py

         os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
 
     from django.contrib.auth.models import User
+    from django import db
+    db.reset_queries()
 
     # check that the username is valid
     kwargs = {'username': req.user, 'is_active': True}
     if superuser_only:
         kwargs['is_superuser'] = True
     try:
-        user = User.objects.get(**kwargs)
-    except User.DoesNotExist:
-        return apache.HTTP_UNAUTHORIZED
-
-    # check the password and any permission given
-    if user.check_password(req.get_basic_auth_pw()):
-        if permission_name:
-            if user.has_perm(permission_name):
+        try:
+            user = User.objects.get(**kwargs)
+        except User.DoesNotExist:
+            return apache.HTTP_UNAUTHORIZED
+    
+        # check the password and any permission given
+        if user.check_password(req.get_basic_auth_pw()):
+            if permission_name:
+                if user.has_perm(permission_name):
+                    return apache.OK
+                else:
+                    return apache.HTTP_UNAUTHORIZED
+            else:
                 return apache.OK
-            else:
-                return apache.HTTP_UNAUTHORIZED
         else:
-            return apache.OK
-    else:
-        return apache.HTTP_UNAUTHORIZED
+            return apache.HTTP_UNAUTHORIZED
+    finally:
+        db.connection.close()

django/core/context_processors.py

 class PermLookupDict(object):
     def __init__(self, user, module_name):
         self.user, self.module_name = user, module_name
+
     def __repr__(self):
-        return str(self.user.get_permission_list())
+        return str(self.user.get_all_permissions())
+
     def __getitem__(self, perm_name):
         return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
+
     def __nonzero__(self):
         return self.user.has_module_perms(self.module_name)
 
 class PermWrapper(object):
     def __init__(self, user):
         self.user = user
+
     def __getitem__(self, module_name):
         return PermLookupDict(self.user, module_name)

django/core/handlers/modpython.py

     for c in http_response.cookies.values():
         mod_python_req.headers_out.add('Set-Cookie', c.output(header=''))
     mod_python_req.status = http_response.status_code
-    for chunk in http_response.iterator:
-        mod_python_req.write(chunk)
+    try:
+        for chunk in http_response:
+            mod_python_req.write(chunk)
+    finally:
+        http_response.close()
 
 def handler(req):
     # mod_python hooks into this function.

django/core/handlers/wsgi.py

 from django.utils import datastructures
 from django import http
 from pprint import pformat
+from shutil import copyfileobj
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
 
 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
 STATUS_CODE_TEXT = {
     505: 'HTTP VERSION NOT SUPPORTED',
 }
 
+def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
+    """
+    A version of shutil.copyfileobj that will not read more than 'size' bytes.
+    This makes it safe from clients sending more than CONTENT_LENGTH bytes of
+    data in the body.
+    """
+    if not size:
+        return copyfileobj(fsrc, fdst, length)
+    while size > 0:
+        buf = fsrc.read(min(length, size))
+        if not buf:
+            break
+        fdst.write(buf)
+        size -= len(buf)
+
 class WSGIRequest(http.HttpRequest):
     def __init__(self, environ):
         self.environ = environ
         try:
             return self._raw_post_data
         except AttributeError:
-            self._raw_post_data = self.environ['wsgi.input'].read(int(self.environ["CONTENT_LENGTH"]))
+            buf = StringIO()
+            content_length = int(self.environ['CONTENT_LENGTH'])
+            safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
+            self._raw_post_data = buf.getvalue()
+            buf.close()
             return self._raw_post_data
 
     GET = property(_get_get, _set_get)
         for c in response.cookies.values():
             response_headers.append(('Set-Cookie', c.output(header='')))
         start_response(status, response_headers)
-        return response.iterator
+        return response

django/core/management.py

                         try:
                             f = opts.get_field(fn)
                         except models.FieldDoesNotExist:
-                            e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
+                            if not hasattr(cls, fn):
+                                e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
                         if fn not in opts.admin.list_display:
                             e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
                 # list_filter
 
     return len(e.errors)
 
-def validate(outfile=sys.stdout):
+def validate(outfile=sys.stdout, silent_success=False):
     "Validates all installed models."
     try:
         num_errors = get_validation_errors(outfile)
+        if silent_success and num_errors == 0:
+            return
         outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
     except ImproperlyConfigured:
         outfile.write("Skipping validation because things aren't configured properly.")
         sys.stderr.write(s.read())
         sys.exit(1)
 
-def runserver(addr, port, use_reloader=True):
+def runserver(addr, port, use_reloader=True, admin_media_dir=''):
     "Starts a lightweight Web server for development."
     from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
     from django.core.handlers.wsgi import WSGIHandler
         print "Development server is running at http://%s:%s/" % (addr, port)
         print "Quit the server with %s." % quit_command
         try:
-            run(addr, int(port), AdminMediaHandler(WSGIHandler()))
+            import django
+            path = admin_media_dir or django.__path__[0] + '/contrib/admin/media'
+            handler = AdminMediaHandler(WSGIHandler(), path)
+            run(addr, int(port), handler)
         except WSGIServerException, e:
             # Use helpful error messages instead of ugly tracebacks.
             ERRORS = {
         autoreload.main(inner_run)
     else:
         inner_run()
-runserver.args = '[--noreload] [optional port number, or ipaddr:port]'
+runserver.args = '[--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port]'
 
 def createcachetable(tablename):
     "Creates the table needed to use the SQL cache backend"
         help='Tells Django to NOT use the auto-reloader when running the development server.')
     parser.add_option('--verbosity', action='store', dest='verbosity', default='2',
         type='choice', choices=['0', '1', '2'],
-        help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
+        help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
+    parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Lets you manually specify the directory to serve admin media from when running the development server.'),
 
     options, args = parser.parse_args(argv[1:])
 
                 addr, port = args[1].split(':')
             except ValueError:
                 addr, port = '', args[1]
-        action_mapping[action](addr, port, options.use_reloader)
+        action_mapping[action](addr, port, options.use_reloader, options.admin_media_path)
     elif action == 'runfcgi':
         action_mapping[action](args[1:])
     else:
         from django.db import models
+        validate(silent_success=True)
         try:
             mod_list = [models.get_app(app_label) for app_label in args[1:]]
         except ImportError, e:

django/core/serializers/json.py

     Convert a queryset to JSON.
     """
     def end_serialization(self):
-        simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder)
+        simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options)
         
     def getvalue(self):
         return self.stream.getvalue()

django/core/servers/basehttp.py

     Use this ONLY LOCALLY, for development! This hasn't been tested for
     security and is not super efficient.
     """
-    def __init__(self, application):
+    def __init__(self, application, media_dir = None):
         from django.conf import settings
-        import django
         self.application = application
-        self.media_dir = django.__path__[0] + '/contrib/admin/media'
+        if not media_dir:
+            import django
+            self.media_dir = django.__path__[0] + '/contrib/admin/media'
+        else:
+            self.media_dir = media_dir
         self.media_url = settings.ADMIN_MEDIA_PREFIX
 
     def __call__(self, environ, start_response):

django/core/servers/fastcgi.py

         print message
     return False
 
-def runfastcgi(argset, **kwargs):
+def runfastcgi(argset=[], **kwargs):
     options = FASTCGI_OPTIONS.copy()
     options.update(kwargs)
     for x in argset:

django/core/validators.py

     catch 'motherfucker' as well. Raises a ValidationError such as:
         Watch your mouth! The words "f--k" and "s--t" are not allowed here.
     """
-    bad_words = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit'] # all in lower case
     field_data = field_data.lower() # normalize
-    words_seen = [w for w in bad_words if field_data.find(w) > -1]
+    words_seen = [w for w in settings.PROFANITIES_LIST if field_data.find(w) > -1]
     if words_seen:
         from django.utils.text import get_text_list
         plural = len(words_seen) > 1
             float(data)
         except ValueError:
             raise ValidationError, gettext("Please enter a valid decimal number.")
-        if len(data) > (self.max_digits + 1):
+        # Negative floats require more space to input.
+        max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1)
+        if len(data) > max_allowed_length:
             raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
                 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
-        if (not '.' in data and len(data) > (self.max_digits - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
+        if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
             raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
                 "Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
         if '.' in data and len(data.split('.')[1]) > self.decimal_places:

django/core/xheaders.py

     """
     Adds the "X-Object-Type" and "X-Object-Id" headers to the given
     HttpResponse according to the given model and object_id -- but only if the
-    given HttpRequest object has an IP address within the INTERNAL_IPS setting.
+    given HttpRequest object has an IP address within the INTERNAL_IPS setting
+    or if the request is from a logged in staff member.
     """
     from django.conf import settings
-    if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
+    if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff):
         response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
         response['X-Object-Id'] = str(object_id)

django/db/backends/util.py

 def dictfetchmany(cursor, number):
     "Returns a certain number of rows from a cursor as a dict"
     desc = cursor.description
-    return [_dict_helper(desc, row) for row in cursor.fetchmany(number)]
+    for row in cursor.fetchmany(number):
+        yield _dict_helper(desc, row)
 
 def dictfetchall(cursor):
     "Returns all rows from a cursor as a dict"
     desc = cursor.description
-    return [_dict_helper(desc, row) for row in cursor.fetchall()]
+    for row in cursor.fetchall():
+        yield _dict_helper(desc, row)

django/db/models/fields/generic.py

         return self.object_id_field_name
         
     def m2m_reverse_name(self):
-        return self.model._meta.pk.attname
+        return self.object_id_field_name
 
     def contribute_to_class(self, cls, name):
         super(GenericRelation, self).contribute_to_class(cls, name)

django/forms/__init__.py

             (self.get_id(), self.field_name, escape(data))
 
 class CheckboxField(FormField):
-    def __init__(self, field_name, checked_by_default=False, validator_list=None):
+    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
         if validator_list is None: validator_list = []
         self.field_name = field_name
         self.checked_by_default = checked_by_default
-        self.is_required = False # because the validator looks for these
+        self.is_required = is_required
         self.validator_list = validator_list[:]
 
     def render(self, data):
                 checked_html = ' checked="checked"'
             field_name = '%s%s' % (self.field_name, value)
             output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
-                (self.get_id() + value , self.__class__.__name__, field_name, checked_html,
-                self.get_id() + value, choice))
+                (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
+                self.get_id() + escape(value), choice))
         output.append('</ul>')
         return '\n'.join(output)
 
         if validator_list is None: validator_list = []
         self.max_digits, self.decimal_places = max_digits, decimal_places
         validator_list = [self.isValidFloat] + validator_list
-        TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
+        TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list)
 
     def isValidFloat(self, field_data, all_data):
         v = validators.IsValidFloat(self.max_digits, self.decimal_places)
             raise validators.CriticalValidationError, e.messages
 
     def html2python(data):
-        if data:
-            return data.upper() # Should always be stored in upper case
-        else:
-            return None
+        return data.upper() # Should always be stored in upper case
     html2python = staticmethod(html2python)
 
 class CommaSeparatedIntegerField(TextField):
         except validators.ValidationError, e:
             raise validators.CriticalValidationError, e.messages
 
+    def render(self, data):
+        if data is None:
+            data = ''
+        elif isinstance(data, (list, tuple)):
+            data = ','.join(data)
+        return super(CommaSeparatedIntegerField, self).render(data)
+
 class RawIdAdminField(CommaSeparatedIntegerField):
     def html2python(data):
-        return data.split(',')
+        if data:
+            return data.split(',')
+        else:
+            return []
     html2python = staticmethod(html2python)
 
 class XMLLargeTextField(LargeTextField):

django/http/__init__.py

         if not mimetype:
             mimetype = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, settings.DEFAULT_CHARSET)
         if hasattr(content, '__iter__'):
-            self._iterator = content
+            self._container = content
             self._is_string = False
         else:
-            self._iterator = [content]
+            self._container = [content]
             self._is_string = True
         self.headers = {'Content-Type': mimetype}
         self.cookies = SimpleCookie()
         self.cookies[key]['max-age'] = 0
 
     def _get_content(self):
-        content = ''.join(self._iterator)
+        content = ''.join(self._container)
         if isinstance(content, unicode):
             content = content.encode(self._charset)
         return content
 
     def _set_content(self, value):
-        self._iterator = [value]
+        self._container = [value]
         self._is_string = True
 
     content = property(_get_content, _set_content)
 
-    def _get_iterator(self):
-        "Output iterator. Converts data into client charset if necessary."
-        for chunk in self._iterator:
-            if isinstance(chunk, unicode):
-                chunk = chunk.encode(self._charset)
-            yield chunk
+    def __iter__(self):
+        self._iterator = self._container.__iter__()
+        return self
 
-    iterator = property(_get_iterator)
+    def next(self):
+        chunk = self._iterator.next()
+        if isinstance(chunk, unicode):
+            chunk = chunk.encode(self._charset)
+        return chunk
+
+    def close(self):
+        if hasattr(self._container, 'close'):
+            self._container.close()
 
     # The remaining methods partially implement the file-like object interface.
     # See http://docs.python.org/lib/bltin-file-objects.html
     def write(self, content):
         if not self._is_string:
             raise Exception, "This %s instance is not writable" % self.__class__
-        self._iterator.append(content)
+        self._container.append(content)
 
     def flush(self):
         pass
     def tell(self):
         if not self._is_string:
             raise Exception, "This %s instance cannot tell its position" % self.__class__
-        return sum([len(chunk) for chunk in self._iterator])
+        return sum([len(chunk) for chunk in self._container])
 
 class HttpResponseRedirect(HttpResponse):
     def __init__(self, redirect_to):

django/middleware/common.py

                 is_internal = referer and (domain in referer)
                 path = request.get_full_path()
                 if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
+                    ua = request.META.get('HTTP_USER_AGENT','<none>')
                     mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
-                        "Referrer: %s\nRequested URL: %s\n" % (referer, request.get_full_path()))
+                        "Referrer: %s\nRequested URL: %s\nUser Agent: %s\n" % (referer, request.get_full_path(), ua))
                 return response
 
         # Use ETags, if requested.

django/middleware/doc.py

     """
     def process_view(self, request, view_func, view_args, view_kwargs):
         """
-        If the request method is HEAD and the IP is internal, quickly return
-        with an x-header indicating the view function.  This is used by the
-        documentation module to lookup the view function for an arbitrary page.
+        If the request method is HEAD and either the IP is internal or the
+        user is a logged-in staff member, quickly return with an x-header
+        indicating the view function.  This is used by the documentation module
+        to lookup the view function for an arbitrary page.
         """
-        if request.method == 'HEAD' and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
+        if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff)):
             response = http.HttpResponse()
             response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
             return response

django/template/defaultfilters.py

 
 def addslashes(value):
     "Adds slashes - useful for passing strings to JavaScript, for example."
-    return value.replace('"', '\\"').replace("'", "\\'")
+    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
 
 def capfirst(value):
     "Capitalizes the first character of the value"

django/template/defaulttags.py

         return ''
 
 class CycleNode(Node):
-    def __init__(self, cyclevars):
+    def __init__(self, cyclevars, variable_name=None):
         self.cyclevars = cyclevars
         self.cyclevars_len = len(cyclevars)
         self.counter = -1
+        self.variable_name = variable_name
 
     def render(self, context):
         self.counter += 1
-        return self.cyclevars[self.counter % self.cyclevars_len]
+        value = self.cyclevars[self.counter % self.cyclevars_len]
+        if self.variable_name:
+            context[self.variable_name] = value
+        return value
 
 class DebugNode(Node):
     def render(self, context):
         self._last_seen = None
 
     def render(self, context):
+        if context.has_key('forloop') and context['forloop']['first']:
+            self._last_seen = None
         content = self.nodelist.render(context)
         if content != self._last_seen:
             firstloop = (self._last_seen == None)
             raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
         cyclevars = [v for v in args[1].split(",") if v]    # split and kill blanks
         name = args[3]
-        node = CycleNode(cyclevars)
+        node = CycleNode(cyclevars, name)
 
         if not hasattr(parser, '_namedCycleNodes'):
             parser._namedCycleNodes = {}

django/utils/datastructures.py

                 pass
         raise KeyError
 
+    def __contains__(self, key):
+        return self.has_key(key)
+
     def get(self, key, default):
         try:
             return self[key]

django/utils/text.py

     return zbuf.getvalue()
 
 ustring_re = re.compile(u"([\u0080-\uffff])")
-def javascript_quote(s):
+
+def javascript_quote(s, quote_double_quotes=False):
 
     def fix(match):
         return r"\u%04x" % ord(match.group(1))
     elif type(s) != unicode:
         raise TypeError, s
     s = s.replace('\\', '\\\\')
+    s = s.replace('\r', '\\r')
     s = s.replace('\n', '\\n')
     s = s.replace('\t', '\\t')
     s = s.replace("'", "\\'")
+    if quote_double_quotes:
+        s = s.replace('"', '&quot;')
     return str(ustring_re.sub(fix, s))
 
 smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')

docs/authentication.txt

         # ...
     my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
 
+We are using this particular test as a relatively simple example, however be
+aware that if you just want to test if a permission is available to a user,
+you can use the ``permission_required()`` decorator described below.
+
 Here's the same thing, using Python 2.4's decorator syntax::
 
     from django.contrib.auth.decorators import user_passes_test
     def my_view(request):
         # ...
 
+The permission_required decorator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Since checking whether a user has a particular permission available to them is a
+relatively common operation, Django provides a shortcut for that particular
+case: the ``permission_required()`` decorator. Using this decorator, the
+earlier example can be written as::
+
+    from django.contrib.auth.decorators import permission_required
+     
+    def my_view(request):
+        # ...
+     
+    my_view = permission_required('polls.can_vote')(my_view)
+
+Note that ``permission_required()`` also takes an optional ``login_url``
+parameter.
+
 Limiting access to generic views
 --------------------------------
 
 Messages are used by the Django admin after successful actions. For example,
 ``"The poll Foo was created successfully."`` is a message.
 
-The API is simple::
+The API is simple:
 
     * To create a new message, use
       ``user_obj.message_set.create(message='message_text')``.

docs/contributing.txt

 
 The tests cover:
 
-    * Models and the database API (``tests/testapp/models``).
-    * The cache system (``tests/otherthests/cache.py``).
-    * The ``django.utils.dateformat`` module (``tests/othertests/dateformat.py``).
-    * Database typecasts (``tests/othertests/db_typecasts.py``).
-    * The template system (``tests/othertests/templates.py`` and
-      ``tests/othertests/defaultfilters.py``).
-    * ``QueryDict`` objects (``tests/othertests/httpwrappers.py``).
-    * Markup template tags (``tests/othertests/markup.py``).
-    * The ``django.utils.timesince`` module (``tests/othertests/timesince.py``).
+    * Models and the database API (``tests/modeltests/``).
+    * The cache system (``tests/regressiontests/cache.py``).
+    * The ``django.utils.dateformat`` module (``tests/regressiontests/dateformat/``).
+    * Database typecasts (``tests/regressiontests/db_typecasts/``).
+    * The template system (``tests/regressiontests/templates/`` and
+      ``tests/regressiontests/defaultfilters/``).
+    * ``QueryDict`` objects (``tests/regressiontests/httpwrappers/``).
+    * Markup template tags (``tests/regressiontests/markup/``).
 
 We appreciate any and all contributions to the test suite!
 
+The Django tests all use the testing infrastructure that ships with Django for
+testing applications. See `Testing Django Applications`_ for an explanation of
+how to write new tests.
+
+.. _Testing Django Applications: http://www.djangoproject.com/documentation/testing/
+
 Running the unit tests
 ----------------------
 
 
 Yes, the unit tests need a settings module, but only for database connection
 info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``.
+You will also need a ``ROOT_URLCONF`` setting (it's value is ignored; it just
+needs to be present) and a ``SITE_ID`` setting (any integer value will do) in
+order for all the tests to pass.
 
-The unit tests will not touch your database; they create a new database, called
-``django_test_db``, which is deleted when the tests are finished. This means
-your user account needs permission to execute ``CREATE DATABASE``.
+The unit tests will not touch your existing databases; they create a new
+database, called ``django_test_db``, which is deleted when the tests are
+finished. This means your user account needs permission to execute ``CREATE
+DATABASE``.
 
 Requesting features
 ===================
 --------------------------
 
 Both ends of a many-to-many relationship get automatic API access to the other
-end. The API works just as a "backward" one-to-many relationship. See _Backward
+end. The API works just as a "backward" one-to-many relationship. See Backward_
 above.
 
 The only difference is in the attribute naming: The model that defines the

docs/django-admin.txt

 
 **New in Django development version**
 
-Inform django-admin that the user should NOT be prompted for any input. Useful if
-the django-admin script will be executed as an unattended, automated script.
+Inform django-admin that the user should NOT be prompted for any input. Useful
+if the django-admin script will be executed as an unattended, automated
+script.
 
 --noreload
 ----------
 will be printed to the console. '0' is no output, '1' is normal output,
 and `2` is verbose output.
 
+--adminmedia
+------------
+
+**New in Django development version**
+
+Example usage::
+    django-admin.py manage.py --adminmedia=/tmp/new-admin-style/
+
+Tell Django where to find the various stylesheets and Javascript files for the
+admin interface when running the development server. Normally these files are
+served out of the Django source tree, but since some designers change these
+files for their site, this option allows you to test against custom versions.
+
 Extra niceties
 ==============
 
     {% endblock %}
 
 Before we get back to the problems with these naive set of views, let's go over
-some salient points of the above template::
+some salient points of the above template:
 
     * Field "widgets" are handled for you: ``{{ form.field }}`` automatically
       creates the "right" type of widget for the form, as you can see with the
       If you must use tables, use tables. If you're a semantic purist, you can
       probably find better HTML than in the above template.
 
-    * To avoid name conflicts, the ``id``s of form elements take the form
-      "id_*fieldname*".
+    * To avoid name conflicts, the ``id`` values of form elements take the
+      form "id_*fieldname*".
 
 By creating a creation form we've solved problem number 3 above, but we still
 don't have any validation. Let's revise the validation issue by writing a new
 Also, because consistency in user interfaces is important, we strongly urge you
 to put punctuation at the end of your validation messages.
 
+When Are Validators Called?
+---------------------------
+
+After a form has been submitted, Django first checks to see that all the
+required fields are present and non-empty. For each field that passes that
+test *and if the form submission contained data* for that field, all the
+validators for that field are called in turn. The emphasised portion in the
+last sentence is important: if a form field is not submitted (because it
+contains no data -- which is normal HTML behaviour), the validators are not
+run against the field.
+
+This feature is particularly important for models using
+``models.BooleanField`` or custom manipulators using things like
+``forms.CheckBoxField``. If the checkbox is not selected, it will not
+contribute to the form submission.
+
+If you would like your validator to *always* run, regardless of whether the
+field it is attached to contains any data, set the ``always_test`` attribute
+on the validator function. For example::
+
+    def my_custom_validator(field_data, all_data):
+        # ...
+
+    my_custom_validator.always_test = True
+
+This validator will always be executed for any field it is attached to.
+
 Ready-made Validators
 ---------------------
 

docs/model-api.txt

 ``editable``
 ~~~~~~~~~~~~
 
-If ``False``, the field will not be editable in the admin. Default is  ``True``.
+If ``False``, the field will not be editable in the admin or via form
+processing using the object's ``AddManipulator`` or ``ChangeManipulator``
+classes. Default is  ``True``.
 
 ``help_text``
 ~~~~~~~~~~~~~

docs/serialization.txt

 .. _json: http://json.org/
 .. _simplejson: http://undefined.org/python/#simplejson
 
+Notes For Specific Serialization Formats
+----------------------------------------
+
+json
+~~~~
+
+If you are using UTF-8 (or any other non-ASCII encoding) data with the JSON
+serializer, you must pass ``ensure_ascii=False`` as a parameter to the
+``serialize()`` call. Otherwise the output will not be encoded correctly.
+
+For example::
+
+    json_serializer = serializers.get_serializer("json")
+    json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
+
 Writing custom serializers
 ``````````````````````````
 

docs/settings.txt

 only used if ``CommonMiddleware`` is installed (see the `middleware docs`_).
 See also ``APPEND_SLASH``.
 
+PROFANITIES_LIST
+----------------
+
+A list of profanities that will trigger a validation error when the
+``hasNoProfanities`` validator is called.
+
 ROOT_URLCONF
 ------------
 

docs/templates_python.txt

 Shortcut for simple tags
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
-Many template tags take a single argument -- a string or a template variable
-reference -- and return a string after doing some processing based solely on
+Many template tags take a number of arguments -- strings or a template variables
+-- and return a string after doing some processing based solely on
 the input argument and some external information. For example, the
 ``current_time`` tag we wrote above is of this variety: we give it a format
 string, it returns the time as a string.
 
 To ease the creation of the types of tags, Django provides a helper function,
 ``simple_tag``. This function, which is a method of
-``django.template.Library``, takes a function that accepts one argument, wraps
-it in a ``render`` function and the other necessary bits mentioned above and
-registers it with the template system.
+``django.template.Library``, takes a function that accepts any number of
+arguments, wraps it in a ``render`` function and the other necessary bits
+mentioned above and registers it with the template system.
 
 Our earlier ``current_time`` function could thus be written like this::
 
         ...
 
 A couple of things to note about the ``simple_tag`` helper function:
-    * Only the (single) argument is passed into our function.
     * Checking for the required number of arguments, etc, has already been
       done by the time our function is called, so we don't need to do that.
     * The quotes around the argument (if any) have already been stripped away,
       so we just receive a plain string.
+    * If the argument was a template variable, our function is passed the
+      current value of the variable, not the variable itself.
+
+When your template tag does not need access to the current context, writing a
+function to work with the input values and using the ``simple_tag`` helper is
+the easiest way to create a new tag.
 
 Inclusion tags
 ~~~~~~~~~~~~~~

tests/regressiontests/defaultfilters/tests.py

 >>> addslashes('"double quotes" and \'single quotes\'')
 '\\"double quotes\\" and \\\'single quotes\\\''
 
+>>> addslashes(r'\ : backslashes, too')
+'\\\\ : backslashes, too'
+
 >>> capfirst('hello world')
 'Hello world'
 

tests/regressiontests/templates/tests.py

             'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
             'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
             'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
+            'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
 
             ### EXCEPTIONS ############################################################
 
             'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,2,3) }, '123'),
             'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,3) }, '13'),
             'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,1) }, '1'),
+            'ifchanged04': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 2, 3), 'numx': (2, 2, 2)}, '122232'),
+            'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'),
+            'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
+            'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
 
             ### IFEQUAL TAG ###########################################################
             'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),