diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index c5e4aa5408..565f72cfaf 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -196,6 +196,7 @@ MIDDLEWARE_CLASSES = ( SESSION_COOKIE_NAME = 'hotclub' # Cookie name. This can be whatever you want. SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. +SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request. ######### # CACHE # diff --git a/django/middleware/sessions.py b/django/middleware/sessions.py index 8b9f21f78d..df7473e904 100644 --- a/django/middleware/sessions.py +++ b/django/middleware/sessions.py @@ -1,4 +1,4 @@ -from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN +from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN, SESSION_SAVE_EVERY_REQUEST from django.models.core import sessions from django.utils.cache import patch_vary_headers import datetime @@ -67,7 +67,7 @@ class SessionMiddleware: modified = request.session.modified except AttributeError: modified = False - if modified: + if modified or SESSION_SAVE_EVERY_REQUEST: 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)) diff --git a/docs/authentication.txt b/docs/authentication.txt index 63beb43031..f9093c81e2 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -49,10 +49,10 @@ Fields and can contain any character. * ``is_staff`` -- Boolean. Designates whether this user can access the admin site. - * ``is_active`` -- Boolean. Designates whether this user account is valid. - Set this to ``False`` instead of deleting accounts. - * ``is_superuser`` -- Boolean. Designates whether this user has permission - to do anything (according to the permission system). + * ``is_active`` -- Boolean. Designates whether this user can log into the + Django admin. Set this to ``False`` instead of deleting accounts. + * ``is_superuser`` -- Boolean. Designates that this user has all permissions + without explicitly assigning them. * ``last_login`` -- A datetime of the user's last login. Is set to the current date/time by default. * ``date_joined`` -- A datetime designating when the account was created. @@ -93,10 +93,11 @@ methods: the user has, both through group and user permissions. * ``has_perm(perm)`` -- Returns ``True`` if the user has the specified - permission. + permission, where perm is in the format ``"package.codename"``. * ``has_perms(perm_list)`` -- Returns ``True`` if the user has each of the - specified permissions. + specified permissions, where each perm is in the format + ``"package.codename"``. * ``has_module_perms(package_name)`` -- Returns ``True`` if the user has any permissions in the given package (the Django app label). @@ -274,6 +275,14 @@ As a shortcut, you can use the convenient ``user_passes_test`` decorator:: from django.views.decorators.auth import user_passes_test + def my_view(request): + # ... + my_view = user_passes_test(my_view, lambda u: u.has_perm('polls.can_vote')) + +Here's the same thing, using Python 2.4's decorator syntax:: + + from django.views.decorators.auth import user_passes_test + @user_passes_test(lambda u: u.has_perm('polls.can_vote')) def my_view(request): # ... diff --git a/docs/model-api.txt b/docs/model-api.txt index e58df9df01..344f34ef2c 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -963,6 +963,13 @@ Now, every ``Pizza`` object will have a ``is_disgusting()`` method. Note that the scope of custom methods is modified to be the same as the module scope. These methods do NOT have access to globals within your model's module. +Additionally, custom methods have access to a few commonly-used objects for +convenience: + + * The ``datetime`` module from Python's standard library. + * The ``db`` object from ``django.core.db``. This represents the database + connection, so you can do custom queries via a cursor object. See + "Executing custom SQL" below. See `Giving models custom methods`_ for a full example. @@ -1056,6 +1063,39 @@ method that begins with "validate":: if int(field_data) in BAD_CUSTOMER_IDS: raise validators.ValidationError, "We don't deliver to this customer." +Executing custom SQL +-------------------- + +Feel free to write custom SQL statements in custom model methods and +module-level methods. Each custom method automatically has access to the +variable ``db``, which is the current database connection. To use it, call +``db.cursor()`` to get a cursor object. Then, call ``cursor.execute(sql, [params])`` +to execute the SQL and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return +the resulting rows. Example:: + + def my_custom_sql(self): + cursor = db.cursor() + cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz]) + row = cursor.fetchone() + return row + +If your custom SQL statement alters the data in your database -- for example, +via a ``DELETE`` or ``UPDATE`` -- you'll need to call ``db.commit()``. Example:: + + def my_custom_sql2(self): + cursor = db.cursor() + cursor.execute("DELETE FROM bar WHERE baz = %s", [self.baz]) + db.commit() + +``db`` and ``cursor`` simply use the standard `Python DB-API`_. If you're not +familiar with the Python DB-API, note that the SQL statement in +``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters +directly within the SQL. If you use this technique, the underlying database +library will automatically add quotes and escaping to your parameter(s) as +necessary. + +.. _Python DB-API: http://www.python.org/peps/pep-0249.html + Using models ============ diff --git a/docs/sessions.txt b/docs/sessions.txt index a070eda2dd..c7d3cd9bfc 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -41,7 +41,8 @@ It implements the following standard dictionary methods: Example: ``request.session['fav_color'] = 'blue'`` * ``__delitem__(key)`` - Example: ``del request.session['fav_color']`` + Example: ``del request.session['fav_color']``. This raises ``KeyError`` + if the given ``key`` isn't already in the session. * ``get(key, default=None)`` Example: ``fav_color = request.session.get('fav_color', 'red')`` @@ -158,10 +159,42 @@ This is necessary because the dictionary is stored in an encoded format:: >>> s.get_decoded() {'user_id': 42} -Session cookies -=============== +When sessions are saved +======================= -A few `Django settings`_ give you control over the session cookie: +By default, Django only saves to the session database when the session has been +modified -- that is if any of its dictionary values have been assigned or +deleted:: + + # Session is modified. + request.session['foo'] = 'bar' + + # Session is modified. + del request.session['foo'] + + # Session is modified. + request.session['foo'] = {} + + # Gotcha: Session is NOT modified, because this alters + # request.session['foo'] instead of request.session. + request.session['foo']['bar'] = 'baz' + +**Only available in Django development version.** To change this default +behavior, set the ``SESSION_SAVE_EVERY_REQUEST`` setting to ``True``. If +``SESSION_SAVE_EVERY_REQUEST`` is ``True``, Django will save the session to the +database on every single request. + +Note that the session cookie is only sent when a session has been created or +modified. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, the session cookie +will be sent on every request. + +Similarly, the ``expires`` part of a session cookie is updated each time the +session cookie is sent. + +Settings +======== + +A few `Django settings`_ give you control over session behavior: SESSION_COOKIE_AGE ------------------ @@ -189,6 +222,17 @@ The name of the cookie to use for sessions. This can be whatever you want. ``'hotclub'`` is a reference to the Hot Club of France, the band Django Reinhardt played in. +SESSION_SAVE_EVERY_REQUEST +-------------------------- + +Default: ``False`` + +**Only available in Django development version.** + +Whether to save the session data on every request. If this is ``False`` +(default), then the session data will only be saved if it has been modified -- +that is, if any of its dictionary values have been assigned or deleted. + .. _Django settings: http://www.djangoproject.com/documentation/settings/ Technical details diff --git a/docs/settings.txt b/docs/settings.txt index 8098856f85..7fe9a56237 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -533,6 +533,13 @@ See the `session docs`_. ``'hotclub'`` is a reference to the Hot Club of France, the band Django Reinhardt played in. +SESSION_SAVE_EVERY_REQUEST +-------------------------- + +Default: ``False`` + +Whether to save the session data on every request. See the `session docs`_. + SITE_ID ------- diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index 97d9be53b7..0f1792bc62 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -33,7 +33,7 @@ activate the admin site for your installation, do these three things: * Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting. * Run the command ``django-admin.py install admin``. This will create an extra database table that the admin needs. - * Edit your ``myproject.urls`` file and uncomment the line below + * Edit your ``myproject/urls.py`` file and uncomment the line below "Uncomment this for admin:". This file is a URLconf; we'll dig into URLconfs in the next tutorial. For now, all you need to know is that it maps URL roots to applications.