1
0
mirror of https://github.com/django/django.git synced 2025-07-06 18:59:13 +00:00

queryset-refactor: Merged to [6340]

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6341 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-09-15 22:00:35 +00:00
parent ca33d307de
commit 28a4aa6f49
23 changed files with 918 additions and 167 deletions

View File

@ -87,6 +87,7 @@ answer newbie questions, and generally made Django that much better:
Matt Croydon <http://www.postneo.com/>
flavio.curella@gmail.com
Jure Cuhalev <gandalf@owca.info>
John D'Agostino <john.dagostino@gmail.com>
dackze+django@gmail.com
David Danier <goliath.mailinglist@gmx.de>
Dirk Datzert <dummy@habmalnefrage.de>

View File

@ -277,6 +277,8 @@ SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or No
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = '/tmp/' # Directory to store session files if using the file session module
#########
# CACHE #

View File

@ -10,6 +10,10 @@ def authenhandler(req, **kwargs):
# that so that the following import works
os.environ.update(req.subprocess_env)
# apache 2.2 requires a call to req.get_basic_auth_pw() before
# req.user and friends are available.
req.get_basic_auth_pw()
# check for PythonOptions
_str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes')

View File

@ -15,25 +15,43 @@ try:
except NameError:
from sets import Set as set # Python 2.3 fallback
def get_hexdigest(algorithm, salt, raw_password):
"""
Returns a string of the hexdigest of the given plaintext password and salt
using the given algorithm ('md5', 'sha1' or 'crypt').
"""
raw_password, salt = smart_str(raw_password), smart_str(salt)
if algorithm == 'crypt':
try:
import crypt
except ImportError:
raise ValueError('"crypt" password algorithm not supported in this environment')
return crypt.crypt(raw_password, salt)
# The rest of the supported algorithms are supported by hashlib, but
# hashlib is only available in Python 2.5.
try:
import hashlib
except ImportError:
if algorithm == 'md5':
import md5
return md5.new(salt + raw_password).hexdigest()
elif algorithm == 'sha1':
import sha
return sha.new(salt + raw_password).hexdigest()
else:
if algorithm == 'md5':
return hashlib.md5(salt + raw_password).hexdigest()
elif algorithm == 'sha1':
return hashlib.sha1(salt + raw_password).hexdigest()
raise ValueError("Got unknown password algorithm type in password.")
def check_password(raw_password, enc_password):
"""
Returns a boolean of whether the raw_password was correct. Handles
encryption formats behind the scenes.
"""
algo, salt, hsh = enc_password.split('$')
if algo == 'md5':
import md5
return hsh == md5.new(smart_str(salt + raw_password)).hexdigest()
elif algo == 'sha1':
import sha
return hsh == sha.new(smart_str(salt + raw_password)).hexdigest()
elif algo == 'crypt':
try:
import crypt
except ImportError:
raise ValueError, "Crypt password algorithm not supported in this environment."
return hsh == crypt.crypt(smart_str(raw_password), smart_str(salt))
raise ValueError, "Got unknown password algorithm type in password."
return hsh == get_hexdigest(algo, salt, raw_password)
class SiteProfileNotAvailable(Exception):
pass
@ -162,10 +180,10 @@ class User(models.Model):
return full_name.strip()
def set_password(self, raw_password):
import sha, random
import random
algo = 'sha1'
salt = sha.new(str(random.random())).hexdigest()[:5]
hsh = sha.new(salt + smart_str(raw_password)).hexdigest()
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
hsh = get_hexdigest(algo, salt, raw_password)
self.password = '%s$%s$%s' % (algo, salt, hsh)
def check_password(self, raw_password):
@ -176,8 +194,7 @@ class User(models.Model):
# Backwards-compatibility check. Older passwords won't include the
# algorithm or salt.
if '$' not in self.password:
import md5
is_correct = (self.password == md5.new(smart_str(raw_password)).hexdigest())
is_correct = (self.password == get_hexdigest('md5', '', raw_password))
if is_correct:
# Convert the password to the new, more secure format.
self.set_password(raw_password)

View File

@ -0,0 +1,143 @@
import base64
import md5
import os
import random
import sys
import time
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
try:
import cPickle as pickle
except ImportError:
import pickle
class SessionBase(object):
"""
Base class for all Session classes.
"""
TEST_COOKIE_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked'
def __init__(self, session_key=None):
self._session_key = session_key
self.accessed = False
self.modified = False
def __contains__(self, key):
return key in self._session
def __getitem__(self, key):
return self._session[key]
def __setitem__(self, key, value):
self._session[key] = value
self.modified = True
def __delitem__(self, key):
del self._session[key]
self.modified = True
def keys(self):
return self._session.keys()
def items(self):
return self._session.items()
def get(self, key, default=None):
return self._session.get(key, default)
def pop(self, key, *args):
return self._session.pop(key, *args)
def set_test_cookie(self):
self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
def test_cookie_worked(self):
return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE
def delete_test_cookie(self):
del self[self.TEST_COOKIE_NAME]
def encode(self, session_dict):
"Returns the given session dictionary pickled and encoded as a string."
pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5)
def decode(self, session_data):
encoded_data = base64.decodestring(session_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
raise SuspiciousOperation("User tampered with session cookie.")
try:
return pickle.loads(pickled)
# Unpickling can cause a variety of exceptions. If something happens,
# just return an empty dictionary (an empty session).
except:
return {}
def _get_new_session_key(self):
"Returns session key that isn't being used."
# The random module is seeded when this Apache child is created.
# Use settings.SECRET_KEY as added salt.
while 1:
session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1),
os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest()
if not self.exists(session_key):
break
return session_key
def _get_session_key(self):
if self._session_key:
return self._session_key
else:
self._session_key = self._get_new_session_key()
return self._session_key
def _set_session_key(self, session_key):
self._session_key = session_key
session_key = property(_get_session_key, _set_session_key)
def _get_session(self):
# Lazily loads session from storage.
self.accessed = True
try:
return self._session_cache
except AttributeError:
if self.session_key is None:
self._session_cache = {}
else:
self._session_cache = self.load()
return self._session_cache
_session = property(_get_session)
# Methods that child classes must implement.
def exists(self, session_key):
"""
Returns True if the given session_key already exists.
"""
raise NotImplementedError
def save(self):
"""
Saves the session data.
"""
raise NotImplementedError
def delete(self, session_key):
"""
Clears out the session data under this key.
"""
raise NotImplementedError
def load(self):
"""
Loads the session data and returns a dictionary.
"""
raise NotImplementedError

View File

@ -0,0 +1,26 @@
from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase
from django.core.cache import cache
class SessionStore(SessionBase):
"""
A cache-based session store.
"""
def __init__(self, session_key=None):
self._cache = cache
super(SessionStore, self).__init__(session_key)
def load(self):
session_data = self._cache.get(self.session_key)
return session_data or {}
def save(self):
self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
def exists(self, session_key):
if self._cache.get(session_key):
return True
return False
def delete(self, session_key):
self._cache.delete(session_key)

View File

@ -0,0 +1,49 @@
from django.conf import settings
from django.contrib.sessions.models import Session
from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation
import datetime
class SessionStore(SessionBase):
"""
Implements database session store
"""
def __init__(self, session_key=None):
super(SessionStore, self).__init__(session_key)
def load(self):
try:
s = Session.objects.get(
session_key = self.session_key,
expire_date__gt=datetime.datetime.now()
)
return self.decode(s.session_data)
except (Session.DoesNotExist, SuspiciousOperation):
# Create a new session_key for extra security.
self.session_key = self._get_new_session_key()
self._session_cache = {}
# Save immediately to minimize collision
self.save()
return {}
def exists(self, session_key):
try:
Session.objects.get(session_key=session_key)
except Session.DoesNotExist:
return False
return True
def save(self):
Session.objects.create(
session_key = self.session_key,
session_data = self.encode(self._session),
expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
)
def delete(self, session_key):
try:
Session.objects.get(session_key=session_key).delete()
except Session.DoesNotExist:
pass

View File

@ -0,0 +1,67 @@
import os
from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation
class SessionStore(SessionBase):
"""
Implements a file based session store.
"""
def __init__(self, session_key=None):
self.storage_path = settings.SESSION_FILE_PATH
self.file_prefix = settings.SESSION_COOKIE_NAME
super(SessionStore, self).__init__(session_key)
def _key_to_file(self, session_key=None):
"""
Get the file associated with this session key.
"""
if session_key is None:
session_key = self.session_key
# Make sure we're not vulnerable to directory traversal. Session keys
# should always be md5s, so they should never contain directory components.
if os.path.sep in session_key:
raise SuspiciousOperation("Invalid characters (directory components) in session key")
return os.path.join(self.storage_path, self.file_prefix + session_key)
def load(self):
session_data = {}
try:
session_file = open(self._key_to_file(), "rb")
try:
session_data = self.decode(session_file.read())
except(EOFError, SuspiciousOperation):
self._session_key = self._get_new_session_key()
self._session_cache = {}
self.save()
finally:
session_file.close()
except(IOError):
pass
return session_data
def save(self):
try:
f = open(self._key_to_file(self.session_key), "wb")
try:
f.write(self.encode(self._session))
finally:
f.close()
except(IOError, EOFError):
pass
def exists(self, session_key):
if os.path.exists(self._key_to_file(session_key)):
return True
return False
def delete(self, session_key):
try:
os.unlink(self._key_to_file(session_key))
except OSError:
pass
def clean(self):
pass

View File

@ -1,6 +1,4 @@
from django.conf import settings
from django.contrib.sessions.models import Session
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers
from email.Utils import formatdate
import datetime
@ -9,73 +7,11 @@ import time
TEST_COOKIE_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked'
class SessionWrapper(object):
def __init__(self, session_key):
self.session_key = session_key
self.accessed = False
self.modified = False
def __contains__(self, key):
return key in self._session
def __getitem__(self, key):
return self._session[key]
def __setitem__(self, key, value):
self._session[key] = value
self.modified = True
def __delitem__(self, key):
del self._session[key]
self.modified = True
def keys(self):
return self._session.keys()
def items(self):
return self._session.items()
def get(self, key, default=None):
return self._session.get(key, default)
def pop(self, key, *args):
self.modified = self.modified or key in self._session
return self._session.pop(key, *args)
def set_test_cookie(self):
self[TEST_COOKIE_NAME] = TEST_COOKIE_VALUE
def test_cookie_worked(self):
return self.get(TEST_COOKIE_NAME) == TEST_COOKIE_VALUE
def delete_test_cookie(self):
del self[TEST_COOKIE_NAME]
def _get_session(self):
# Lazily loads session from storage.
self.accessed = True
try:
return self._session_cache
except AttributeError:
if self.session_key is None:
self._session_cache = {}
else:
try:
s = Session.objects.get(session_key=self.session_key,
expire_date__gt=datetime.datetime.now())
self._session_cache = s.get_decoded()
except (Session.DoesNotExist, SuspiciousOperation):
self._session_cache = {}
# Set the session_key to None to force creation of a new
# key, for extra security.
self.session_key = None
return self._session_cache
_session = property(_get_session)
class SessionMiddleware(object):
def process_request(self, request):
request.session = SessionWrapper(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None))
engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
request.session = engine.SessionStore(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None))
def process_response(self, request, response):
# If request.session was modified, or if response.session was set, save
@ -89,25 +25,22 @@ class SessionMiddleware(object):
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
if request.session.session_key:
session_key = request.session.session_key
else:
obj = Session.objects.get_new_session_object()
session_key = obj.session_key
if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
max_age = None
expires = None
else:
max_age = settings.SESSION_COOKIE_AGE
rfcdate = formatdate(time.time() + settings.SESSION_COOKIE_AGE)
# Fixed length date must have '-' separation in the format
# DD-MMM-YYYY for compliance with Netscape cookie standard
expires = (rfcdate[:7] + "-" + rfcdate[8:11]
+ "-" + rfcdate[12:26] + "GMT")
new_session = Session.objects.save(session_key, request.session._session,
datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
response.set_cookie(settings.SESSION_COOKIE_NAME, session_key,
expires = datetime.datetime.strftime(datetime.datetime.utcnow() + \
datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT")
# Save the seesion data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME, request.session.session_key,
max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
return response

View File

@ -1,4 +1,4 @@
import base64, md5, random, sys, datetime, os, time
import base64, md5, random, sys, datetime
import cPickle as pickle
from django.db import models
from django.utils.translation import ugettext_lazy as _
@ -74,6 +74,7 @@ class Session(models.Model):
session_data = models.TextField(_('session data'))
expire_date = models.DateTimeField(_('expire date'))
objects = SessionManager()
class Meta:
db_table = 'django_session'
verbose_name = _('session')

View File

@ -1,35 +1,59 @@
r"""
>>> s = SessionWrapper(None)
Inject data into the session cache.
>>> s._session_cache = {}
>>> s._session_cache['some key'] = 'exists'
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession
>>> s.accessed
>>> db_session = DatabaseSession()
>>> db_session.modified
False
>>> s.modified
False
>>> s.pop('non existant key', 'does not exist')
>>> db_session['cat'] = "dog"
>>> db_session.modified
True
>>> db_session.pop('cat')
'dog'
>>> db_session.pop('some key', 'does not exist')
'does not exist'
>>> s.accessed
>>> db_session.save()
>>> db_session.exists(db_session.session_key)
True
>>> s.modified
>>> db_session.delete(db_session.session_key)
>>> db_session.exists(db_session.session_key)
False
>>> s.pop('some key')
'exists'
>>> s.accessed
>>> file_session = FileSession()
>>> file_session.modified
False
>>> file_session['cat'] = "dog"
>>> file_session.modified
True
>>> s.modified
True
>>> s.pop('some key', 'does not exist')
>>> file_session.pop('cat')
'dog'
>>> file_session.pop('some key', 'does not exist')
'does not exist'
>>> file_session.save()
>>> file_session.exists(file_session.session_key)
True
>>> file_session.delete(file_session.session_key)
>>> file_session.exists(file_session.session_key)
False
>>> cache_session = CacheSession()
>>> cache_session.modified
False
>>> cache_session['cat'] = "dog"
>>> cache_session.modified
True
>>> cache_session.pop('cat')
'dog'
>>> cache_session.pop('some key', 'does not exist')
'does not exist'
>>> cache_session.save()
>>> cache_session.delete(cache_session.session_key)
>>> cache_session.exists(cache_session.session_key)
False
"""
from django.contrib.sessions.middleware import SessionWrapper
if __name__ == '__main__':
import doctest
doctest.testmod()

View File

@ -1,6 +1,9 @@
import os
from django.conf import settings
from django.core import signals
from django.core.exceptions import ImproperlyConfigured
from django.dispatch import dispatcher
from django.utils.functional import curry
__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
@ -8,12 +11,19 @@ if not settings.DATABASE_ENGINE:
settings.DATABASE_ENGINE = 'dummy'
try:
backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
# Most of the time, the database backend will be one of the official
# backends that ships with Django, so look there first.
_import_path = 'django.db.backends.'
backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
except ImportError, e:
# If the import failed, we might be looking for a database backend
# distributed external to Django. So we'll try that next.
try:
_import_path = ''
backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
except ImportError, e_user:
# The database backend wasn't found. Display a helpful error message
# listing all possible database backends.
from django.core.exceptions import ImproperlyConfigured
import os
# listing all possible (built-in) database backends.
backend_dir = os.path.join(__path__[0], 'backends')
available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
available_backends.sort()
@ -23,10 +33,21 @@ except ImportError, e:
else:
raise # If there's some other error, this must be an error in Django itself.
get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, {}, {}, [''])
get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, {}, {}, ['']).runshell()
def _import_database_module(import_path='', module_name=''):
"""Lazyily import a database module when requested."""
return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
# We don't want to import the introspect/creation modules unless
# someone asks for 'em, so lazily load them on demmand.
get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
get_creation_module = curry(_import_database_module, _import_path, 'creation')
# We want runshell() to work the same way, but we have to treat it a
# little differently (since it just runs instead of returning a module like
# the above) and wrap the lazily-loaded runshell() method.
runshell = lambda: _import_database_module(_import_path, "client").runshell()
# Convenient aliases for backend bits.
connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
DatabaseError = backend.DatabaseError
IntegrityError = backend.IntegrityError

View File

@ -4,8 +4,6 @@ from cStringIO import StringIO
from urlparse import urlparse
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.contrib.sessions.models import Session
from django.contrib.sessions.middleware import SessionWrapper
from django.core.handlers.base import BaseHandler
from django.core.handlers.wsgi import WSGIRequest
from django.core.signals import got_request_exception
@ -132,9 +130,10 @@ class Client:
def _session(self):
"Obtain the current session variables"
if 'django.contrib.sessions' in settings.INSTALLED_APPS:
engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
if cookie:
return SessionWrapper(cookie.value)
return engine.SessionStore(cookie.value)
return {}
session = property(_session)
@ -247,24 +246,23 @@ class Client:
"""
user = authenticate(**credentials)
if user and user.is_active and 'django.contrib.sessions' in settings.INSTALLED_APPS:
obj = Session.objects.get_new_session_object()
engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
# Create a fake request to store login details
request = HttpRequest()
request.session = SessionWrapper(obj.session_key)
request.session = engine.SessionStore()
login(request, user)
# Set the cookie to represent the session
self.cookies[settings.SESSION_COOKIE_NAME] = obj.session_key
self.cookies[settings.SESSION_COOKIE_NAME] = request.session.session_key
self.cookies[settings.SESSION_COOKIE_NAME]['max-age'] = None
self.cookies[settings.SESSION_COOKIE_NAME]['path'] = '/'
self.cookies[settings.SESSION_COOKIE_NAME]['domain'] = settings.SESSION_COOKIE_DOMAIN
self.cookies[settings.SESSION_COOKIE_NAME]['secure'] = settings.SESSION_COOKIE_SECURE or None
self.cookies[settings.SESSION_COOKIE_NAME]['expires'] = None
# Set the session values
Session.objects.save(obj.session_key, request.session._session,
datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
# Save the session values
request.session.save()
return True
else:
@ -275,9 +273,6 @@ class Client:
Causes the authenticated user to be logged out.
"""
try:
Session.objects.get(session_key=self.cookies['sessionid'].value).delete()
except KeyError:
pass
session = __import__(settings.SESSION_ENGINE, {}, {}, ['']).SessionStore()
session.delete(session_key=self.cookies['sessionid'].value)
self.cookies = SimpleCookie()

View File

@ -16,7 +16,7 @@ def set_language(request):
redirect to the page in the request (the 'next' parameter) without changing
any state.
"""
next = request.GET.get('next', None)
next = request.REQUEST.get('next', None)
if not next:
next = request.META.get('HTTP_REFERER', None)
if not next:

View File

@ -21,7 +21,7 @@ file, you'll need to use mod_python's ``PythonAuthenHandler`` directive along
with the standard ``Auth*`` and ``Require`` directives::
<Location /example/>
AuthType basic
AuthType Basic
AuthName "example.com"
Require valid-user
@ -29,6 +29,49 @@ with the standard ``Auth*`` and ``Require`` directives::
PythonAuthenHandler django.contrib.auth.handlers.modpython
</Location>
.. admonition:: Using the authentication handler with Apache 2.2
If you're using Apache 2.2, you'll need to take a couple extra steps.
You'll need to ensure that ``mod_auth_basic`` and ``mod_authz_user``
are loaded. These might be compiled staticly into Apache, or you might
need to use ``LoadModule`` to load them dynamically (as shown in the
example at the bottom of this note).
You'll also need to insert configuration directives that prevent Apache
from trying to use other authentication modules. Depnding on which other
authentication modules you have loaded, you might need one or more of
the following directives::
AuthBasicAuthoritative Off
AuthDefaultAuthoritative Off
AuthzLDAPAuthoritative Off
AuthzDBMAuthoritative Off
AuthzDefaultAuthoritative Off
AuthzGroupFileAuthoritative Off
AuthzOwnerAuthoritative Off
AuthzUserAuthoritative Off
A complete configuration, with differences between Apache 2.0 and
Apache 2.2 marked in bold, would look something like:
.. parsed-literal::
**LoadModule auth_basic_module modules/mod_auth_basic.so**
**LoadModule authz_user_module modules/mod_authz_user.so**
...
<Location /exmaple/>
AuthType Basic
AuthName "example.com"
**AuthBasicAuthoritative Off**
Require valid-user
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonAuthenHandler django.contrib.auth.handlers.modpython
</Location>
By default, the authentication handler will limit access to the ``/example/``
location to users marked as staff members. You can use a set of
``PythonOption`` directives to modify this behavior:

View File

@ -524,6 +524,15 @@ 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.)
If you want to use headers to disable caching altogether,
``django.views.decorators.never_cache`` is a view decorator that adds
headers to ensure the response won't be cached by browsers or other caches. Example::
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
...
.. _`Cache-Control spec`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
Other optimizations

258
docs/contenttypes.txt Normal file
View File

@ -0,0 +1,258 @@
==========================
The contenttypes framework
==========================
Django includes a "contenttypes" application that can track all of
the models installed in your Django-powered project, providing a
high-level, generic interface for working with your models.
Overview
========
At the heart of the contenttypes application is the ``ContentType``
model, which lives at
``django.contrib.contenttypes.models.ContentType``. Instances of
``ContentType`` represent and store information about the models
installed in your project, and new instances of ``ContentType`` are
automatically created whenever new models are installed.
Instances of ``ContentType`` have methods for returning the model
classes they represent and for querying objects from those models.
``ContentType`` also has a `custom manager`_ that adds methods for
working with ``ContentType`` and for obtaining instances of
``ContentType`` for a particular model.
Relations between your models and ``ContentType`` can also be used to
enable "generic" relationships between an instance of one of your
models and instances of any model you have installed.
.. _custom manager: ../model-api/#custom-managers
Installing the contenttypes framework
=====================================
The contenttypes framework is included in the default
``INSTALLED_APPS`` list created by ``django-admin.py startproject``,
but if you've removed it or if you manually set up your
``INSTALLED_APPS`` list, you can enable it by adding
``'django.contrib.contenttypes'`` to your ``INSTALLED_APPS`` setting.
It's generally a good idea to have the contenttypes framework
installed; several of Django's other bundled applications require it:
* The admin application uses it to log the history of each object
added or changed through the admin interface.
* Django's `authentication framework`_ uses it to tie user permissions
to specific models.
* Django's comments system (``django.contrib.comments``) uses it to
"attach" comments to any installed model.
.. _authentication framework: ../authentication/
The ``ContentType`` model
=========================
Each instance of ``ContentType`` has three fields which, taken
together, uniquely describe an installed model:
``app_label``
The name of the application the model is part of. This is taken from
the ``app_label`` attribute of the model, and includes only the *last*
part of the application's Python import path;
"django.contrib.contenttypes", for example, becomes an ``app_label``
of "contenttypes".
``model``
The name of the model class.
``name``
The human-readable name of the model. This is taken from
`the verbose_name attribute`_ of the model.
Let's look at an example to see how this works. If you already have
the contenttypes application installed, and then add `the sites
application`_ to your ``INSTALLED_APPS`` setting and run ``manage.py
syncdb`` to install it, the model ``django.contrib.sites.models.Site``
will be installed into your database. Along with it a new instance
of ``ContentType`` will be created with the following values:
* ``app_label`` will be set to ``'sites'`` (the last part of the Python
path "django.contrib.sites").
* ``model`` will be set to ``'site'``.
* ``name`` will be set to ``'site'``.
.. _the verbose_name attribute: ../model-api/#verbose_name
.. _the sites application: ../sites/
Methods on ``ContentType`` instances
====================================
Each ``ContentType`` instance has methods that allow you to get from a
``ContentType`` instance to the model it represents, or to retrieve objects
from that model:
``get_object_for_this_type(**kwargs)``
Takes a set of valid `lookup arguments`_ for the model the
``ContentType`` represents, and does `a get() lookup`_ on that
model, returning the corresponding object.
``model_class()``
Returns the model class represented by this ``ContentType``
instance.
For example, we could look up the ``ContentType`` for the ``User`` model::
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
>>> user_type
<ContentType: user>
And then use it to query for a particular ``User``, or to get access
to the ``User`` model class::
>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
Together, ``get_object_for_this_type`` and ``model_class`` enable two
extremely important use cases:
1. Using these methods, you can write high-level generic code that
performs queries on any installed model -- instead of importing and
using a single specific model class, you can pass an ``app_label``
and ``model`` into a ``ContentType`` lookup at runtime, and then
work with the model class or retrieve objects from it.
2. You can relate another model to ``ContentType`` as a way of tying
instances of it to particular model classes, and use these methods
to get access to those model classes.
Several of Django's bundled applications make use of the latter
technique. For example, `the permissions system`_ in Django's
authentication framework uses a ``Permission`` model with a foreign
key to ``ContentType``; this lets ``Permission`` represent concepts
like "can add blog entry" or "can delete news story".
.. _lookup arguments: ../db-api/#field-lookups
.. _a get() lookup: ../db-api/#get-kwargs
.. _the permissions system: ../authentication/#permissions
The ``ContentTypeManager``
--------------------------
``ContentType`` also has a custom manager, ``ContentTypeManager``,
which adds the following methods:
``clear_cache()``
Clears an internal cache used by ``ContentType`` to keep track of which
models for which it has created ``ContentType`` instances. You probably
won't ever need to call this method yourself; Django will call it
automatically when it's needed.
``get_for_model(model)``
Takes either a model class or an instance of a model, and returns the
``ContentType`` instance representing that model.
The ``get_for_model`` method is especially useful when you know you
need to work with a ``ContentType`` but don't want to go to the
trouble of obtaining the model's metadata to perform a manual lookup::
>>> from django.contrib.auth.models import User
>>> user_type = ContentType.objects.get_for_model(User)
>>> user_type
<ContentType: user>
Generic relations
=================
Adding a foreign key from one of your own models to ``ContentType``
allows your model to effectively tie itself to another model class, as
in the example of the ``Permission`` model above. But it's possible to
go one step further and use ``ContentType`` to enable truly generic
(sometimes called "polymorphic") relationships between models.
A simple example is a tagging system, which might look like this::
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag
A normal ``ForeignKey`` can only "point to" one other model, which
means that if the ``TaggedItem`` model used a ``ForeignKey`` it would have to
choose one and only one model to store tags for. The contenttypes
application provides a special field type --
``django.contrib.contenttypes.generic.GenericForeignKey`` -- which
works around this and allows the relationship to be with any
model. There are three parts to setting up a ``GenericForeignKey``:
1. Give your model a ``ForeignKey`` to ``ContentType``.
2. Give your model a field that can store a primary-key value from the
models you'll be relating to. (For most models, this means an
``IntegerField`` or ``PositiveIntegerField``.)
3. Give your model a ``GenericForeignKey``, and pass it the names of
the two fields described above. If these fields are named
"content_type" and "object_id", you can omit this -- those are the
default field names ``GenericForeignKey`` will look for.
This will enable an API similar to the one used for a normal ``ForeignKey``;
each ``TaggedItem`` will have a ``content_object`` field that returns the
object it's related to, and you can also assign to that field or use it when
creating a ``TaggedItem``::
>>> from django.contrib.models.auth import User
>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()
>>> t.content_object
<User: Guido>
Reverse generic relations
-------------------------
If you know which models you'll be using most often, you can also add
a "reverse" generic relationship to enable an additional API. For example::
class Bookmark(models.Model):
url = models.URLField()
tags = generic.GenericRelation(TaggedItem)
``Bookmark`` instances will each have a ``tags`` attribute, which can
be used to retrieve their associated ``TaggedItems``::
>>> b = Bookmark('http://www.djangoproject.com/')
>>> b.save()
>>> t1 = TaggedItem(content_object=b, tag='django')
>>> t1.save()
>>> t2 = TaggedItem(content_object=b, tag='python')
>>> t2.save()
>>> b.tags.all()
[<TaggedItem: django>, <TaggedItem: python>]
If you don't add the reverse relationship, you can do the lookup manually::
>>> b = Bookmark.objects.get(url='http://www.djangoproject.com/)
>>> bookmark_type = ContentType.objects.get_for_model(b)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id,
... object_id=b.id)
[<TaggedItem: django>, <TaggedItem: python>]
Note that if you delete an object that has a ``GenericRelation``, any objects
which have a ``GenericForeignKey`` pointing at it will be deleted as well. In
the example above, this means that if a ``Bookmark`` object were deleted, any
``TaggedItem`` objects pointing at it would be deleted at the same time.

View File

@ -951,6 +951,23 @@ Example::
If you pass ``in_bulk()`` an empty list, you'll get an empty dictionary.
``iterator()``
~~~~~~~~~~~~
Evaluates the ``QuerySet`` (by performing the query) and returns an
`iterator`_ over the results. A ``QuerySet`` typically reads all of
its results and instantiates all of the corresponding objects the
first time you access it; ``iterator()`` will instead read results and
instantiate objects in discrete chunks, yielding them one at a
time. For a ``QuerySet`` which returns a large number of objects, this
often results in better performance and a significant reduction in
memory use.
Note that using ``iterator()`` on a ``QuerySet`` which has already
been evaluated will force it to evaluate again, repeating the query.
.. _iterator: http://www.python.org/dev/peps/pep-0234/
``latest(field_name=None)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -178,7 +178,8 @@ A date field. Has a few extra optional arguments:
====================== ===================================================
The admin represents this as an ``<input type="text">`` with a JavaScript
calendar and a shortcut for "Today."
calendar, and a shortcut for "Today." The JavaScript calendar will always start
the week on a Sunday.
``DateTimeField``
~~~~~~~~~~~~~~~~~

View File

@ -10,18 +10,21 @@ Cookies contain a session ID -- not the data itself.
Enabling sessions
=================
Sessions are implemented via a piece of middleware_ and a Django model.
Sessions are implemented via a piece of middleware_.
To enable session functionality, do these two things:
To enable session functionality, do the following:
* Edit the ``MIDDLEWARE_CLASSES`` setting and make sure
``MIDDLEWARE_CLASSES`` contains ``'django.contrib.sessions.middleware.SessionMiddleware'``.
The default ``settings.py`` created by ``django-admin.py startproject`` has
``SessionMiddleware`` activated.
* Add ``'django.contrib.sessions'`` to your ``INSTALLED_APPS`` setting, and
run ``manage.py syncdb`` to install the single database table that stores
session data.
* Add ``'django.contrib.sessions'`` to your ``INSTALLED_APPS`` setting,
and run ``manage.py syncdb`` to install the single database table
that stores session data.
**New in development version**: this step is optional if you're not using
the database session backend; see `configuring the session engine`_.
If you don't want to use sessions, you might as well remove the
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
@ -29,6 +32,44 @@ from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
.. _middleware: ../middleware/
Configuring the session engine
==============================
**New in development version**.
By default, Django stores sessions in your database (using the model
``django.contrib.sessions.models.Session``). Though this is convenient, in
some setups it's faster to store session data elsewhere, so Django can be
configured to store session data on your filesystem or in your cache.
Using file-based sessions
-------------------------
To use file-based sessions, set the ``SESSION_ENGINE`` setting to
``"django.contrib.sessions.backends.file"``.
You might also want to set the ``SESSION_FILE_PATH`` setting (which
defaults to ``/tmp``) to control where Django stores session files. Be
sure to check that your web server has permissions to read and write to
this location.
Using cache-based sessions
--------------------------
To store session data using Django's cache system, set ``SESSION_ENGINE``
to ``"django.contrib.sessions.backends.cache"``. You'll want to make sure
you've configured your cache; see the `cache documentation`_ for details.
.. _cache documentation: ../cache/
.. note::
You probably don't want to use cache-based sessions if you're not using
the memcached cache backend. The local memory and simple cache backends
don't retain data long enough to be good choices, and it'll be faster
to use file or database sessions directly instead of sending everything
through the file or database cache backends.
Using sessions in views
=======================
@ -153,7 +194,18 @@ Here's a typical usage example::
Using sessions out of views
===========================
Internally, each session is just a normal Django model. The ``Session`` model
The ``SessionStore`` which implements the session storage method can be imported
and a API is available to manipulate the session data outside of a view::
>>> from django.contrib.sessions.engines.db import SessionStore
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10)
>>> s['last_login']
datetime.datetime(2005, 8, 20, 13, 35, 0)
>>> s.save()
Or if you are using the ``django.contrib.sessions.engine.db`` each
session is just a normal Django model. The ``Session`` model
is defined in ``django/contrib/sessions/models.py``. Because it's a normal
model, you can access sessions using the normal Django database API::
@ -245,6 +297,31 @@ Settings
A few `Django settings`_ give you control over session behavior:
SESSION_ENGINE
--------------
**New in Django development version**
Default: ``django.contrib.sessions.backends.db``
Controls where Django stores session data. Valid values are:
* ``'django.contrib.sessions.backends.db'``
* ``'django.contrib.sessions.backends.file'``
* ``'django.contrib.sessions.backends.cache'``
See `configuring the session engine`_ for more details.
SESSION_FILE_PATH
-----------------
**New in Django development version**
Default: ``/tmp/``
If you're using file-based session storage, this sets the directory in
which Django will store session data.
SESSION_COOKIE_AGE
------------------

View File

@ -253,9 +253,14 @@ DATABASE_ENGINE
Default: ``''`` (Empty string)
The database backend to use. Either ``'postgresql_psycopg2'``,
``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'``,
``'oracle'``, or ``'ado_mssql'``.
The database backend to use. The build-in database backends are
``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``,
``'sqlite3'``, ``'oracle'``, or ``'ado_mssql'``.
You can also use a database backend that doesn't ship with Django by
setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e.
``mypackage.backends.whatever``). Writing a whole new database backend from
scratch is left as an exercise to the reader.
DATABASE_HOST
-------------
@ -728,6 +733,21 @@ Default: ``'root@localhost'``
The e-mail address that error messages come from, such as those sent to
``ADMINS`` and ``MANAGERS``.
SESSION_ENGINE
--------------
**New in Django development version**
Default: ``django.contrib.sessions.backends.db``
Controls where Django stores session data. Valid values are:
* ``'django.contrib.sessions.backends.db'``
* ``'django.contrib.sessions.backends.file'``
* ``'django.contrib.sessions.backends.cache'``
See the `session docs`_ for more details.
SESSION_COOKIE_AGE
------------------
@ -770,6 +790,17 @@ Default: ``False``
Whether to expire the session when the user closes his or her browser.
See the `session docs`_.
SESSION_FILE_PATH
-----------------
**New in Django development version**
Default: ``/tmp/``
If you're using file-based session storage, this sets the directory in
which Django will store session data. See the `session docs`_ for
more details.
SESSION_SAVE_EVERY_REQUEST
--------------------------

View File

@ -555,6 +555,38 @@ template loaders that come with Django:
Django uses the template loaders in order according to the ``TEMPLATE_LOADERS``
setting. It uses each loader until a loader finds a match.
The ``render_to_string()`` shortcut
===================================
To cut down on the repetitive nature of loading and rendering
templates, Django provides a shortcut function which largely
automates the process: ``render_to_string()`` in
``django.template.loader``, which loads a template, renders it and
returns the resulting string::
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', { 'foo': 'bar' })
The ``render_to_string`` shortcut takes one required argument --
``template_name``, which should be the name of the template to load
and render -- and two optional arguments::
dictionary
A dictionary to be used as variables and values for the
template's context. This can also be passed as the second
positional argument.
context_instance
An instance of ``Context`` or a subclass (e.g., an instance of
``RequestContext``) to use as the template's context. This can
also be passed as the third positional argument.
See also the `render_to_response()`_ shortcut, which calls
``render_to_string`` and feeds the result into an ``HttpResponse``
suitable for returning directly from a view.
.. _render_to_response(): ../shortcuts/#render-to-response
Extending the template system
=============================