Browse files

Merged to trunk r1039

git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@1041 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
2 parents 18f43e5 + 23bb8c4 commit 204443cf4cb442ed1789e4379140a3af83448112 @rjwittams rjwittams committed Nov 1, 2005
View
8 django/core/db/__init__.py
@@ -1,16 +1,11 @@
"""
This is the core database connection.
-All CMS code assumes database SELECT statements cast the resulting values as such:
+All Django code assumes database SELECT statements cast the resulting values as such:
* booleans are mapped to Python booleans
* dates are mapped to Python datetime.date objects
* times are mapped to Python datetime.time objects
* timestamps are mapped to Python datetime.datetime objects
-
-Right now, we're handling this by using psycopg's custom typecast definitions.
-If we move to a different database module, we should ensure that it either
-performs the appropriate typecasting out of the box, or that it has hooks that
-let us do that.
"""
from django.conf.settings import DATABASE_ENGINE
@@ -41,6 +36,7 @@
get_random_function_sql = dbmod.get_random_function_sql
get_table_list = dbmod.get_table_list
get_relations = dbmod.get_relations
+quote_name = dbmod.quote_name
OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
DATA_TYPES = dbmod.DATA_TYPES
DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE
View
4 django/core/db/backends/ado_mssql.py
@@ -110,6 +110,10 @@ def get_table_list(cursor):
def get_relations(cursor, table_name):
raise NotImplementedError
+def quote_name(name):
+ # TODO: Figure out how MS-SQL quotes database identifiers.
+ return name
+
OPERATOR_MAPPING = {
'exact': '=',
'iexact': 'LIKE',
View
11 django/core/db/backends/mysql.py
@@ -122,18 +122,23 @@ def get_table_list(cursor):
def get_relations(cursor, table_name):
raise NotImplementedError
+def quote_name(name):
+ if name.startswith("`") and name.endswith("`"):
+ return name # Quoting once is enough.
+ return "`%s`" % name
+
OPERATOR_MAPPING = {
'exact': '=',
'iexact': 'LIKE',
- 'contains': 'LIKE',
+ 'contains': 'LIKE BINARY',
'icontains': 'LIKE',
'ne': '!=',
'gt': '>',
'gte': '>=',
'lt': '<',
'lte': '<=',
- 'startswith': 'LIKE',
- 'endswith': 'LIKE',
+ 'startswith': 'LIKE BINARY',
+ 'endswith': 'LIKE BINARY',
'istartswith': 'LIKE',
'iendswith': 'LIKE',
}
View
5 django/core/db/backends/postgresql.py
@@ -116,6 +116,11 @@ def get_relations(cursor, table_name):
continue
return relations
+def quote_name(name):
+ if name.startswith('"') and name.endswith('"'):
+ return name # Quoting once is enough.
+ return '"%s"' % name
+
# Register these custom typecasts, because Django expects dates/times to be
# in Python's native (standard-library) datetime/time format, whereas psycopg
# use mx.DateTime by default.
View
5 django/core/db/backends/sqlite3.py
@@ -124,6 +124,11 @@ def get_table_list(cursor):
def get_relations(cursor, table_name):
raise NotImplementedError
+def quote_name(name):
+ if name.startswith('"') and name.endswith('"'):
+ return name # Quoting once is enough.
+ return '"%s"' % name
+
# Operators and fields ########################################################
OPERATOR_MAPPING = {
View
13 django/core/template/defaulttags.py
@@ -211,8 +211,12 @@ def __init__(self, filepath, parsed):
self.filepath, self.parsed = filepath, parsed
def render(self, context):
+ from django.conf.settings import DEBUG
if not include_is_allowed(self.filepath):
- return '' # Fail silently for invalid includes.
+ if DEBUG:
+ return "[Didn't have permission to include file]"
+ else:
+ return '' # Fail silently for invalid includes.
try:
fp = open(self.filepath, 'r')
output = fp.read()
@@ -223,8 +227,11 @@ def render(self, context):
try:
t = Template(output)
return t.render(context)
- except TemplateSyntaxError:
- return '' # Fail silently for invalid included templates.
+ except (TemplateSyntaxError, e):
+ if DEBUG:
+ return "[Included template had syntax error: %s]" % e
+ else:
+ return '' # Fail silently for invalid included templates.
return output
class LoadNode(Node):
View
3 django/middleware/sessions.py
@@ -71,6 +71,7 @@ def process_response(self, request, response):
session_key = request.session.session_key or sessions.get_new_session_key()
new_session = sessions.save(session_key, request.session._session,
datetime.datetime.now() + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
+ expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT")
response.set_cookie(SESSION_COOKIE_NAME, session_key,
- max_age=SESSION_COOKIE_AGE, domain=SESSION_COOKIE_DOMAIN)
+ max_age=SESSION_COOKIE_AGE, expires=expires, domain=SESSION_COOKIE_DOMAIN)
return response
View
16 django/utils/cache.py
@@ -22,19 +22,19 @@
from django.core.cache import cache
cc_delim_re = re.compile(r'\s*,\s*')
+
def patch_cache_control(response, **kwargs):
"""
This function patches the Cache-Control header by adding all
keyword arguments to it. The transformation is as follows:
- - all keyword parameter names are turned to lowercase and
- all _ will be translated to -
- - if the value of a parameter is True (exatly True, not just a
- true value), only the parameter name is added to the header
- - all other parameters are added with their value, after applying
- str to it.
+ * All keyword parameter names are turned to lowercase, and underscores
+ are converted to hyphens.
+ * If the value of a parameter is True (exactly True, not just a
+ true value), only the parameter name is added to the header.
+ * All other parameters are added with their value, after applying
+ str() to it.
"""
-
def dictitem(s):
t = s.split('=',1)
if len(t) > 1:
@@ -49,9 +49,7 @@ def dictvalue(t):
return t[0] + '=' + str(t[1])
if response.has_header('Cache-Control'):
- print response['Cache-Control']
cc = cc_delim_re.split(response['Cache-Control'])
- print cc
cc = dict([dictitem(el) for el in cc])
else:
cc = {}
View
4 django/utils/httpwrappers.py
@@ -172,9 +172,9 @@ def has_header(self, header):
return True
return False
- def set_cookie(self, key, value='', max_age=None, path='/', domain=None, secure=None):
+ def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
self.cookies[key] = value
- for var in ('max_age', 'path', 'domain', 'secure'):
+ for var in ('max_age', 'path', 'domain', 'secure', 'expires'):
val = locals()[var]
if val is not None:
self.cookies[key][var.replace('_', '-')] = val
View
73 docs/cache.txt
@@ -272,39 +272,64 @@ and a list/tuple of header names as its second argument.
.. _`HTTP Vary headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
-Controlling cache: Using Vary headers
-=====================================
+Controlling cache: Using other headers
+======================================
+
+Another problem with caching is the privacy of data and the question of where
+data should be stored in a cascade of caches.
+
+A user usually faces two kinds of caches: his own browser cache (a private
+cache) and his provider's cache (a public cache). A public cache is used by
+multiple users and controlled by someone else. This poses problems with
+sensitive data: You don't want, say, your banking-account number stored in a
+public cache. So Web applications need a way to tell caches which data is
+private and which is public.
+
+The solution is to indicate a page's cache should be "private." To do this in
+Django, use the ``cache_control`` view decorator. Example::
+
+ from django.views.decorators.cache import cache_control
+ @cache_control(private=True)
+ def my_view(request):
+ ...
+
+This decorator takes care of sending out the appropriate HTTP header behind the
+scenes.
-Another problem with caching is the privacy of data, and the question where data can
-be stored in a cascade of caches. A user usually faces two kinds of caches: his own
-browser cache (a private cache) and his providers cache (a public cache). A public cache
-is used by multiple users and controlled by someone else. This poses problems with private
-(in the sense of sensitive) data - you don't want your social security number or your
-banking account numbers stored in some public cache. So web applications need a way
-to tell the caches what data is private and what is public.
+There are a few other ways to control cache parameters. For example, HTTP
+allows applications to do the following:
-Other aspects are the definition how long a page should be cached at max, or wether the
-cache should allways check for newer versions and only deliver the cache content when
-there were no changes (some caches might deliver cached content even if the server page
-changed - just because the cache copy isn't yet expired).
+ * Define the maximum time a page should be cached.
+ * Specify whether a cache should always check for newer versions, only
+ delivering the cached content when there are no changes. (Some caches
+ might deliver cached content even if the server page changed -- simply
+ because the cache copy isn't yet expired.)
-So there are a multitude of options you can control for your pages. This is where the
-Cache-Control header (more infos in `HTTP Cache-Control headers`_) comes in. The usage
-is quite simple::
+In Django, use the ``cache_control`` view decorator to specify these cache
+parameters. In this example, ``cache_control`` tells caches to revalidate the
+cache on every access and to store cached versions for, at most, 3600 seconds::
- @cache_control(private=True, must_revalidate=True, max_age=3600)
+ from django.views.decorators.cache import cache_control
+ @cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
...
-This would define the view as private, to be revalidated on every access and cache
-copies will only be stored for 3600 seconds at max.
+Any valid ``Cache-Control`` directive is valid in ``cache_control()``. For a
+full list, see the `Cache-Control spec`_. Just pass the directives as keyword
+arguments to ``cache_control()``, substituting underscores for hyphens. For
+directives that don't take an argument, set the argument to ``True``.
+
+Examples:
+
+ * ``@cache_control(max_age=3600)`` turns into ``max-age=3600``.
+ * ``@cache_control(public=True)`` turns into ``public``.
-The caching middleware already set's this header up with a max-age of the CACHE_MIDDLEWARE_SETTINGS
-setting. And the cache_page decorator does the same. The cache_control decorator correctly merges
-different values into one big header, though. But you should take into account that middlewares
-might overwrite some of your headers or set their own defaults if you don't give that header yourself.
+(Note that the caching middleware already sets the cache header's max-age with
+the value of the ``CACHE_MIDDLEWARE_SETTINGS`` setting. If you use a custom
+``max_age`` in a ``cache_control`` decorator, the decorator will take
+precedence, and the header values will be merged correctly.)
-.. _`HTTP Cache-Control headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+.. _`Cache-Control spec`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
Other optimizations
===================
View
15 docs/db-api.txt
@@ -161,9 +161,9 @@ The DB API supports the following lookup types:
``foo``, ``FOO``, ``fOo``, etc.
contains Case-sensitive containment test:
``polls.get_list(question__contains="spam")`` returns all polls
- that contain "spam" in the question. (PostgreSQL only. MySQL
- doesn't support case-sensitive LIKE statements; ``contains``
- will act like ``icontains`` for MySQL.)
+ that contain "spam" in the question. (PostgreSQL and MySQL
+ only. SQLite doesn't support case-sensitive LIKE statements;
+ ``contains`` will act like ``icontains`` for SQLite.)
icontains Case-insensitive containment test.
gt Greater than: ``polls.get_list(id__gt=4)``.
gte Greater than or equal to.
@@ -174,11 +174,10 @@ The DB API supports the following lookup types:
a list of polls whose IDs are either 1, 3 or 4.
startswith Case-sensitive starts-with:
``polls.get_list(question_startswith="Would")``. (PostgreSQL
- only. MySQL doesn't support case-sensitive LIKE statements;
- ``startswith`` will act like ``istartswith`` for MySQL.)
- endswith Case-sensitive ends-with. (PostgreSQL only. MySQL doesn't
- support case-sensitive LIKE statements; ``endswith`` will act
- like ``iendswith`` for MySQL.)
+ and MySQL only. SQLite doesn't support case-sensitive LIKE
+ statements; ``startswith`` will act like ``istartswith`` for
+ SQLite.)
+ endswith Case-sensitive ends-with. (PostgreSQL and MySQL only.)
istartswith Case-insensitive starts-with.
iendswith Case-insensitive ends-with.
range Range test:
View
4 docs/request_response.txt
@@ -284,12 +284,14 @@ Methods
Returns ``True`` or ``False`` based on a case-insensitive check for a
header with the given name.
-``set_cookie(key, value='', max_age=None, path='/', domain=None, secure=None)``
+``set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None)``
Sets a cookie. The parameters are the same as in the `cookie Morsel`_
object in the Python standard library.
* ``max_age`` should be a number of seconds, or ``None`` (default) if
the cookie should last only as long as the client's browser session.
+ * ``expires`` should be a string in the format
+ ``"Wdy, DD-Mon-YY HH:MM:SS GMT"``.
* Use ``domain`` if you want to set a cross-domain cookie. For example,
``domain=".lawrence.com"`` will set a cookie that is readable by
the domains www.lawrence.com, blogs.lawrence.com and
View
3 docs/tutorial04.txt
@@ -213,6 +213,9 @@ The generic views pass ``object`` and ``object_list`` to their templates, so
change your templates so that ``latest_poll_list`` becomes ``object_list`` and
``poll`` becomes ``object``.
+In the ``vote()`` view, change the template call from ``polls/detail`` to
+``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``.
+
Finally, you can delete the ``index()``, ``detail()`` and ``results()`` views
from ``polls/views/polls.py``. We don't need them anymore.
View
16 tests/testapp/models/basic.py
@@ -51,13 +51,29 @@ class Article(meta.Model):
<Article object>
>>> articles.get_object(pub_date__year=2005)
<Article object>
+>>> articles.get_object(pub_date__year=2005, pub_date__month=7)
+<Article object>
+>>> articles.get_object(pub_date__year=2005, pub_date__month=7, pub_date__day=28)
+<Article object>
+
+>>> articles.get_list(pub_date__year=2005)
+[<Article object>]
+>>> articles.get_list(pub_date__year=2004)
+[]
+>>> articles.get_list(pub_date__year=2005, pub_date__month=7)
+[<Article object>]
# Django raises an ArticleDoesNotExist exception for get_object()
>>> articles.get_object(id__exact=2)
Traceback (most recent call last):
...
ArticleDoesNotExist: Article does not exist for {'id__exact': 2}
+>>> articles.get_object(pub_date__year=2005, pub_date__month=8)
+Traceback (most recent call last):
+ ...
+ArticleDoesNotExist: Article does not exist for ...
+
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to articles.get_object(id__exact=1).

0 comments on commit 204443c

Please sign in to comment.