From 58f95c3a6a16b44749fc05fe1747ebf12467a496 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 16:20:24 +0000 Subject: [PATCH 1/7] Fixed #850 -- Made clarification in docs/tutorial02.txt. Thanks, Daniel git-svn-id: http://code.djangoproject.com/svn/django/trunk@1300 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/tutorial02.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From cd01d6d3817fb01f3f1f021abd45abd49dfa019e Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 16:35:44 +0000 Subject: [PATCH 2/7] Fixed #848 -- Made auth docs more clear on permission handling. git-svn-id: http://code.djangoproject.com/svn/django/trunk@1302 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/authentication.txt | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) 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): # ... From 3895a825a9696b58db1a0a2f6f30b1b023d58050 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 17:16:13 +0000 Subject: [PATCH 3/7] Added SESSION_SAVE_EVERY_REQUEST setting. git-svn-id: http://code.djangoproject.com/svn/django/trunk@1303 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/global_settings.py | 1 + django/middleware/sessions.py | 4 +-- docs/sessions.txt | 49 +++++++++++++++++++++++++++++++--- docs/settings.txt | 7 +++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index ad7a0a54f7..a272f01970 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -195,6 +195,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/sessions.txt b/docs/sessions.txt index a070eda2dd..c4058c0163 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,41 @@ 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' + +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 +221,15 @@ 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`` + +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 ------- From 16493e135c21bd45e0f5961d687421176cd4c4cc Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 17:18:25 +0000 Subject: [PATCH 4/7] Added note to docs/sessions.txt about only being available in development version git-svn-id: http://code.djangoproject.com/svn/django/trunk@1304 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/sessions.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/sessions.txt b/docs/sessions.txt index c4058c0163..c7d3cd9bfc 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -179,9 +179,10 @@ deleted:: # request.session['foo'] instead of request.session. request.session['foo']['bar'] = 'baz' -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. +**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 @@ -226,6 +227,8 @@ 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. From 2bb18eddbe6f5ea4e46da330a25a2918b1ca6dc5 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 17:33:40 +0000 Subject: [PATCH 5/7] Added 'Executing custom SQL' section to docs/model-api.txt git-svn-id: http://code.djangoproject.com/svn/django/trunk@1305 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/model-api.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/model-api.txt b/docs/model-api.txt index e58df9df01..3ef6e9803e 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,32 @@ 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 + +Note that ``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 ============ From c472b1471d662b3a3f35641b83086622eae3c10e Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 17:35:29 +0000 Subject: [PATCH 6/7] Small improvement to docs/model-api.txt git-svn-id: http://code.djangoproject.com/svn/django/trunk@1306 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/model-api.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/model-api.txt b/docs/model-api.txt index 3ef6e9803e..d8cc33ba87 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -1079,9 +1079,17 @@ the resulting rows. Example:: row = cursor.fetchone() return row -Note that ``db`` and ``cursor`` simply use the standard `Python DB-API`_. +If your custom SQL statement alters the data in your database -- for example, +via a ``DELETE``, ``INSERT`` or ``UPDATE`` -- you'll need to call +``db.commit()``. example:: -If you're not familiar with the Python DB-API, note that the SQL statement in + 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 From b4bb345b94f7f6415d3a3aa258dbe46478fcfff5 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 20 Nov 2005 17:36:16 +0000 Subject: [PATCH 7/7] Another change to docs/model-api.txt git-svn-id: http://code.djangoproject.com/svn/django/trunk@1307 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/model-api.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/model-api.txt b/docs/model-api.txt index d8cc33ba87..344f34ef2c 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -1080,8 +1080,7 @@ the resulting rows. Example:: return row If your custom SQL statement alters the data in your database -- for example, -via a ``DELETE``, ``INSERT`` or ``UPDATE`` -- you'll need to call -``db.commit()``. example:: +via a ``DELETE`` or ``UPDATE`` -- you'll need to call ``db.commit()``. Example:: def my_custom_sql2(self): cursor = db.cursor()