diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rwxr-xr-x | django/bin/compile-messages.py | 11 | ||||
-rw-r--r-- | django/conf/project_template/settings.py | 4 | ||||
-rw-r--r-- | django/contrib/admin/templates/admin/base.html | 1 | ||||
-rw-r--r-- | django/contrib/admin/views/doc.py | 2 | ||||
-rw-r--r-- | django/contrib/admin/views/main.py | 16 | ||||
-rw-r--r-- | django/core/cache/backends/memcached.py | 2 | ||||
-rw-r--r-- | django/middleware/cache.py | 3 | ||||
-rw-r--r-- | django/middleware/http.py | 24 | ||||
-rw-r--r-- | docs/cache.txt | 5 | ||||
-rw-r--r-- | docs/faq.txt | 10 | ||||
-rw-r--r-- | docs/i18n.txt | 5 | ||||
-rw-r--r-- | docs/middleware.txt | 18 | ||||
-rw-r--r-- | docs/model-api.txt | 55 | ||||
-rw-r--r-- | docs/templates_python.txt | 7 |
15 files changed, 142 insertions, 22 deletions
@@ -68,6 +68,7 @@ answer newbie questions, and generally made Django that much better: deric@monowerks.com dne@mayonnaise.net Jeremy Dunck <http://dunck.us/> + Andy Dustman <farcepest@gmail.com> Clint Ecker gandalf@owca.info Baishampayan Ghose diff --git a/django/bin/compile-messages.py b/django/bin/compile-messages.py index 44a84de379..07dcce7bf6 100755 --- a/django/bin/compile-messages.py +++ b/django/bin/compile-messages.py @@ -11,7 +11,7 @@ def compile_messages(): elif os.path.isdir('locale'): basedir = os.path.abspath('locale') else: - print "this script should be run from the django svn tree or your project or app tree" + print "This script should be run from the Django SVN tree or your project or app tree." sys.exit(1) for dirpath, dirnames, filenames in os.walk(basedir): @@ -19,7 +19,14 @@ def compile_messages(): if f.endswith('.po'): sys.stderr.write('processing file %s in %s\n' % (f, dirpath)) pf = os.path.splitext(os.path.join(dirpath, f))[0] - cmd = 'msgfmt -o "%s.mo" "%s.po"' % (pf, pf) + # Store the names of the .mo and .po files in an environment + # variable, rather than doing a string replacement into the + # command, so that we can take advantage of shell quoting, to + # quote any malicious characters/escaping. + # See http://cyberelk.net/tim/articles/cmdline/ar01s02.html + os.environ['djangocompilemo'] = pf + '.mo' + os.environ['djangocompilepo'] = pf + '.po' + cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"' os.system(cmd) if __name__ == "__main__": diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py index 63e07c061a..d6f34a28db 100644 --- a/django/conf/project_template/settings.py +++ b/django/conf/project_template/settings.py @@ -27,6 +27,10 @@ LANGUAGE_CODE = 'en-us' SITE_ID = 1 +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html index 41514e6a81..b63604b268 100644 --- a/django/contrib/admin/templates/admin/base.html +++ b/django/contrib/admin/templates/admin/base.html @@ -6,6 +6,7 @@ {% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% admin_media_prefix %}css/rtl.css{% endblock %}" />{% endif %} {% block extrastyle %}{% endblock %} {% block extrahead %}{% endblock %} +{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %} </head> {% load i18n %} diff --git a/django/contrib/admin/views/doc.py b/django/contrib/admin/views/doc.py index d1541abee9..b724cc5485 100644 --- a/django/contrib/admin/views/doc.py +++ b/django/contrib/admin/views/doc.py @@ -226,7 +226,7 @@ def model_detail(request, app_label, model_name): return render_to_response('admin_doc/model_detail.html', { 'name': '%s.%s' % (opts.app_label, opts.object_name), - 'summary': "Fields on %s objects" % opts.object_name, + 'summary': _("Fields on %s objects") % opts.object_name, 'description': model.__doc__, 'fields': fields, }, context_instance=RequestContext(request)) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 25f7eeaa70..2a8b135bc7 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -272,7 +272,9 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po post_url_continue += "?_popup=1" return HttpResponseRedirect(post_url_continue % pk_value) if request.POST.has_key("_popup"): - return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %r, "%s");</script>' % \ + if type(pk_value) is str: # Quote if string, so JavaScript doesn't think it's a variable. + pk_value = '"%s"' % pk_value.replace('"', '\\"') + return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ (pk_value, str(new_object).replace('"', '\\"'))) elif request.POST.has_key("_addanother"): request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) @@ -734,9 +736,19 @@ class ChangeList(object): qs = qs.order_by((self.order_type == 'desc' and '-' or '') + lookup_order_field) # Apply keyword searches. + def construct_search(field_name): + if field_name.startswith('^'): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith('='): + return "%s__iexact" % field_name[1:] + elif field_name.startswith('@'): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + if self.lookup_opts.admin.search_fields and self.query: for bit in self.query.split(): - or_queries = [models.Q(**{'%s__icontains' % field_name: bit}) for field_name in self.lookup_opts.admin.search_fields] + or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields] other_qs = QuerySet(self.model) other_qs = other_qs.filter(reduce(operator.or_, or_queries)) qs = qs & other_qs diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py index 86ae096d2c..180f95da73 100644 --- a/django/core/cache/backends/memcached.py +++ b/django/core/cache/backends/memcached.py @@ -20,7 +20,7 @@ class CacheClass(BaseCache): return val def set(self, key, value, timeout=0): - self._cache.set(key, value, timeout) + self._cache.set(key, value, timeout or self.default_timeout) def delete(self, key): self._cache.delete(key) diff --git a/django/middleware/cache.py b/django/middleware/cache.py index 08e77d1375..58800b24da 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -41,6 +41,9 @@ class CacheMiddleware(object): def process_request(self, request): "Checks whether the page is already cached and returns the cached version if available." + if self.cache_anonymous_only: + assert hasattr(request, 'user'), "The Django cache middleware with CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True requires authentication middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware' before the CacheMiddleware." + if not request.method in ('GET', 'HEAD') or request.GET: request._cache_update_cache = False return None # Don't bother checking the cache. diff --git a/django/middleware/http.py b/django/middleware/http.py index 12d0c0f683..3ebd8ffd1a 100644 --- a/django/middleware/http.py +++ b/django/middleware/http.py @@ -35,3 +35,27 @@ class ConditionalGetMiddleware(object): response.content = '' return response + +class SetRemoteAddrFromForwardedFor(object): + """ + Middleware that sets REMOTE_ADDR based on HTTP_X_FORWARDED_FOR, if the + latter is set. This is useful if you're sitting behind a reverse proxy that + causes each request's REMOTE_ADDR to be set to 127.0.0.1. + + Note that this does NOT validate HTTP_X_FORWARDED_FOR. If you're not behind + a reverse proxy that sets HTTP_X_FORWARDED_FOR automatically, do not use + this middleware. Anybody can spoof the value of HTTP_X_FORWARDED_FOR, and + because this sets REMOTE_ADDR based on HTTP_X_FORWARDED_FOR, that means + anybody can "fake" their IP address. Only use this when you can absolutely + trust the value of HTTP_X_FORWARDED_FOR. + """ + def process_request(self, request): + try: + real_ip = request.META['HTTP_X_FORWARDED_FOR'] + except KeyError: + return None + else: + # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. + # Take just the first one. + real_ip = real_ip.split(",")[0] + request.META['REMOTE_ADDR'] = real_ip diff --git a/docs/cache.txt b/docs/cache.txt index 62fec289b9..1795345ed9 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -233,7 +233,10 @@ The cache middleware caches every page that doesn't have GET or POST parameters. Optionally, if the ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting is ``True``, only anonymous requests (i.e., not those made by a logged-in user) will be cached. This is a simple and effective way of disabling caching for any -user-specific pages (include Django's admin interface). +user-specific pages (include Django's admin interface). Note that if you use +``CACHE_MIDDLEWARE_ANONYMOUS_ONLY``, you should make sure you've activated +``AuthenticationMiddleware`` and that ``AuthenticationMiddleware`` appears +before ``CacheMiddleware`` in your ``MIDDLEWARE_CLASSES``. Additionally, ``CacheMiddleware`` automatically sets a few headers in each ``HttpResponse``: diff --git a/docs/faq.txt b/docs/faq.txt index 3cd7090583..42d9ddea55 100644 --- a/docs/faq.txt +++ b/docs/faq.txt @@ -98,11 +98,10 @@ Lawrence, Kansas, USA. On IRC, Simon goes by ``SimonW``. `Wilson Miner`_ - Wilson's design-fu makes us all look like rock stars. When not sneaking - into apartment complex swimming pools, he's the Commercial Development - Director for World Online, which means he makes the money that pays all our - paychecks. He lives in Lawrence, Kansas. - + Wilson's design-fu makes us all look like rock stars. By day, he's an + interactive designer for `Apple`. Don't ask him what he's working on, or + he'll have to kill you. He lives in San Francisco. + On IRC, Wilson goes by ``wilsonian``. .. _`World Online`: http://code.djangoproject.com/wiki/WorldOnline @@ -113,6 +112,7 @@ Lawrence, Kansas, USA. .. _`simon.incutio.com`: http://simon.incutio.com/ .. _`Jacob Kaplan-Moss`: http://www.jacobian.org/ .. _`Wilson Miner`: http://www.wilsonminer.com/ +.. _`Apple`: http://www.apple.com/ Which sites use Django? ----------------------- diff --git a/docs/i18n.txt b/docs/i18n.txt index e12900c2f9..4d0d92b082 100644 --- a/docs/i18n.txt +++ b/docs/i18n.txt @@ -48,9 +48,10 @@ bit of i18n-related overhead in certain places of the framework. If you don't use internationalization, you should take the two seconds to set ``USE_I18N = False`` in your settings file. If ``USE_I18N`` is set to ``False``, then Django will make some optimizations so as not to load the -internationalization machinery. +internationalization machinery. See the `documentation for USE_I18N`_. -See the `documentation for USE_I18N`_. +You'll probably also want to remove ``'django.core.context_processors.i18n'`` +from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting. .. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n diff --git a/docs/middleware.txt b/docs/middleware.txt index bad00fd890..efc4d89569 100644 --- a/docs/middleware.txt +++ b/docs/middleware.txt @@ -63,7 +63,7 @@ Adds a few conveniences for perfectionists: last component in the path contains a period. So ``foo.com/bar`` is redirected to ``foo.com/bar/``, but ``foo.com/bar/file.txt`` is passed through unchanged. - + If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be redirected to the same URL with a leading "www." @@ -101,6 +101,22 @@ Handles conditional GET operations. If the response has a ``ETag`` or Also removes the content from any response to a HEAD request and sets the ``Date`` and ``Content-Length`` response-headers. +django.middleware.http.SetRemoteAddrFromForwardedFor +---------------------------------------------------- + +**New in Django development version** + +Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``, +if the latter is set. This is useful if you're sitting behind a reverse proxy +that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``. + +**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're +not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do +not use this middleware. Anybody can spoof the value of +``HTTP_X_FORWARDED_FOR``, and because this sets ``REMOTE_ADDR`` based on +``HTTP_X_FORWARDED_FOR``, that means anybody can "fake" their IP address. Only +use this when you can absolutely trust the value of ``HTTP_X_FORWARDED_FOR``. + django.contrib.sessions.middleware.SessionMiddleware ---------------------------------------------------- diff --git a/docs/model-api.txt b/docs/model-api.txt index 502ceaf7ff..0a3abe4e26 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -217,7 +217,7 @@ steps: subdirectory of ``MEDIA_ROOT`` it should upload files. 3. All that will be stored in your database is a path to the file - (relative to ``MEDIA_ROOT``). You'll must likely want to use the + (relative to ``MEDIA_ROOT``). You'll most likely want to use the convenience ``get_<fieldname>_url`` function provided by Django. For example, if your ``ImageField`` is called ``mug_shot``, you can get the absolute URL to your image in a template with ``{{ @@ -230,6 +230,14 @@ For example, say your ``MEDIA_ROOT`` is set to ``'/home/media'``, and upload a file on Jan. 15, 2007, it will be saved in the directory ``/home/media/photos/2007/01/15``. +Note that whenever you deal with uploaded files, you should pay close attention +to where you're uploading them and what type of files they are, to avoid +security holes. *Validate all uploaded files* so that you're sure the files are +what you think they are. For example, if you blindly let somebody upload files, +without validation, to a directory that's within your Web server's document +root, then somebody could upload a CGI or PHP script and execute that script by +visiting its URL on your site. Don't allow that. + .. _`strftime formatting`: http://docs.python.org/lib/module-time.html#l2h-1941 ``FilePathField`` @@ -678,8 +686,9 @@ you can use the name of the model, rather than the model object itself:: class Manufacturer(models.Model): # ... -Note, however, that support for strings around model names in ``ForeignKey`` is -quite new, and it can be buggy in some cases. +Note, however, that you can only use strings to refer to models in the same +models.py file -- you cannot use a string to reference a model in a different +application, or to reference a model that has been imported from elsewhere. Behind the scenes, Django appends ``"_id"`` to the field name to create its database column name. In the above example, the database table for the ``Car`` @@ -801,7 +810,10 @@ here's how you'd represent that:: As with ``ForeignKey``, a relationship to self can be defined by using the string ``'self'`` instead of the model name, and you can refer to as-yet -undefined models by using a string containing the model name. +undefined models by using a string containing the model name. However, you +can only use strings to refer to models in the same models.py file -- you +cannot use a string to reference a model in a different application, or to +reference a model that has been imported from elsewhere. It's suggested, but not required, that the name of a ``ManyToManyField`` (``toppings`` in the example above) be a plural describing the set of related @@ -1374,6 +1386,41 @@ user searches for ``john lennon``, Django will do the equivalent of this SQL WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%') AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%') +**New in Django development version:** For faster and/or more restrictive +searches, prefix the field name with an operator: + +``^`` + Matches the beginning of the field. For example, if ``search_fields`` is + set to ``['^first_name', '^last_name']`` and a user searches for + ``john lennon``, Django will do the equivalent of this SQL ``WHERE`` + clause:: + + WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%') + AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%') + + This query is more efficient than the normal ``'%john%'`` query, because + the database only needs to check the beginning of a column's data, rather + than seeking through the entire column's data. Plus, if the column has an + index on it, some databases may be able to use the index for this query, + even though it's a ``LIKE`` query. + +``=`` + Matches exactly, case-insensitive. For example, if + ``search_fields`` is set to ``['=first_name', '=last_name']`` and + a user searches for ``john lennon``, Django will do the equivalent + of this SQL ``WHERE`` clause:: + + WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john') + AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon') + + Note that the query input is split by spaces, so, following this example, + it's not currently not possible to search for all records in which + ``first_name`` is exactly ``'john winston'`` (containing a space). + +``@`` + Performs a full-text match. This is like the default search method but uses + an index. Currently this is only available for MySQL. + Managers ======== diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 95ccfb3eab..62069ffd6a 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -259,9 +259,10 @@ an `HttpRequest object`_ as its first argument. For example:: The second difference is that it automatically populates the context with a few variables, according to your `TEMPLATE_CONTEXT_PROCESSORS setting`_. -The ``TEMPLATE_CONTEXT_PROCESSORS`` setting is a tuple of callables that take a -request object as their argument and return a dictionary of items to be merged -into the context. By default, ``TEMPLATE_CONTEXT_PROCESSORS`` is set to:: +The ``TEMPLATE_CONTEXT_PROCESSORS`` setting is a tuple of callables -- called +**context processors** -- that take a request object as their argument and +return a dictionary of items to be merged into the context. By default, +``TEMPLATE_CONTEXT_PROCESSORS`` is set to:: ("django.core.context_processors.auth", "django.core.context_processors.debug", |