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

multi-auth: Merged to [3051]

git-svn-id: http://code.djangoproject.com/svn/django/branches/multi-auth@3052 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2006-06-02 03:51:30 +00:00
parent aeb807989f
commit 0c341d780e
56 changed files with 582 additions and 362 deletions

View File

@ -45,6 +45,7 @@ answer newbie questions, and generally made Django that much better:
andy@jadedplanet.net andy@jadedplanet.net
Antonio Cavedoni <http://cavedoni.com/> Antonio Cavedoni <http://cavedoni.com/>
C8E C8E
Chris Chamberlin <dja@cdc.msbx.net>
Amit Chakradeo <http://amit.chakradeo.net/> Amit Chakradeo <http://amit.chakradeo.net/>
ChaosKCW ChaosKCW
Ian Clelland <clelland@gmail.com> Ian Clelland <clelland@gmail.com>

View File

@ -235,6 +235,7 @@ SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever yo
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). 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_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. 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.
######### #########
# CACHE # # CACHE #

View File

@ -13,7 +13,7 @@ msgstr ""
"Last-Translator: panos laganakos <panos.laganakos@gmail.com>\n" "Last-Translator: panos laganakos <panos.laganakos@gmail.com>\n"
"Language-Team: Greek\n" "Language-Team: Greek\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: contrib/comments/models.py:67 contrib/comments/models.py:166 #: contrib/comments/models.py:67 contrib/comments/models.py:166

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ try:
import settings # Assumed to be in the same directory. import settings # Assumed to be in the same directory.
except ImportError: except ImportError:
import sys import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -7,7 +7,7 @@ body.login { background:#eee; }
.login #content-main { width:100%; } .login #content-main { width:100%; }
.login form { margin-top:1em; } .login form { margin-top:1em; }
.login .form-row { padding:4px 0; float:left; width:100%; } .login .form-row { padding:4px 0; float:left; width:100%; }
.login .form-row label { float:left; width:7em; padding-right:0.5em; line-height:2em; text-align:right; font-size:1em; color:#333; } .login .form-row label { float:left; width:9em; padding-right:0.5em; line-height:2em; text-align:right; font-size:1em; color:#333; }
.login .form-row #id_username, .login .form-row #id_password { width:16em; } .login .form-row #id_username, .login .form-row #id_password { width:14em; }
.login span.help { font-size:10px; display:block; } .login span.help { font-size:10px; display:block; }
.login .submit-row { clear:both; padding:1em 0 0 7.4em; } .login .submit-row { clear:both; padding:1em 0 0 9.4em; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

View File

@ -12,7 +12,7 @@
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="../../../">{% trans "Home" %}</a> &rsaquo; <a href="../../../">{% trans "Home" %}</a> &rsaquo;
<a href="../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; <a href="../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
{% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|striptags|truncatewords:"18" }}{% endif %} {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18"|escape }}{% endif %}
</div> </div>
{% endif %}{% endblock %} {% endif %}{% endblock %}
{% block content %}<div id="content-main"> {% block content %}<div id="content-main">

View File

@ -2,13 +2,9 @@
<div class="{{ class_names }}" > <div class="{{ class_names }}" >
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %} {% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
{% for bound_field in bound_fields %} {% for bound_field in bound_fields %}
{% if bound_field.has_label_first %} {% if bound_field.has_label_first %}{% field_label bound_field %}{% endif %}
{% field_label bound_field %}
{% endif %}
{% field_widget bound_field %} {% field_widget bound_field %}
{% if not bound_field.has_label_first %} {% if not bound_field.has_label_first %}{% field_label bound_field %}{% endif %}
{% field_label bound_field %}
{% endif %}
{% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %} {% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %}
{% endfor %} {% endfor %}
</div> </div>

View File

@ -3,6 +3,6 @@
<ul> <ul>
{% for choice in choices %} {% for choice in choices %}
<li{% if choice.selected %} class="selected"{% endif %}> <li{% if choice.selected %} class="selected"{% endif %}>
<a href="{{ choice.query_string }}">{{ choice.display }}</a></li> <a href="{{ choice.query_string }}">{{ choice.display|escape }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -0,0 +1,10 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %}
{% block content %}
<p>{% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}</p>
{% endblock %}

View File

@ -24,8 +24,8 @@
{% for action in action_list %} {% for action in action_list %}
<tr> <tr>
<th scope="row">{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th> <th scope="row">{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th>
<td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name }} {{ action.user.last_name }}){% endif %}</td> <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name|escape }} {{ action.user.last_name|escape }}){% endif %}</td>
<td>{{ action.change_message}}</td> <td>{{ action.change_message|escape }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -42,7 +42,12 @@ class AdminApplistNode(template.Node):
}) })
if model_list: if model_list:
model_list.sort() # Sort using verbose decorate-sort-undecorate pattern
# instead of key argument to sort() for python 2.3 compatibility
decorated = [(x['name'], x) for x in model_list]
decorated.sort()
model_list = [x for key, x in decorated]
app_list.append({ app_list.append({
'name': app_label.title(), 'name': app_label.title(),
'has_module_perms': has_module_perms, 'has_module_perms': has_module_perms,

View File

@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
urlpatterns = patterns('', urlpatterns = patterns('',
('^$', 'django.contrib.admin.views.main.index'), ('^$', 'django.contrib.admin.views.main.index'),
('^r/(\d+)/(\d+)/$', 'django.views.defaults.shortcut'), ('^r/(\d+)/(.*)/$', 'django.views.defaults.shortcut'),
('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'django.conf'}), ('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'django.conf'}),
('^logout/$', 'django.contrib.auth.views.logout'), ('^logout/$', 'django.contrib.auth.views.logout'),
('^password_change/$', 'django.contrib.auth.views.password_change'), ('^password_change/$', 'django.contrib.auth.views.password_change'),

View File

@ -35,6 +35,7 @@ ORDER_TYPE_VAR = 'ot'
PAGE_VAR = 'p' PAGE_VAR = 'p'
SEARCH_VAR = 'q' SEARCH_VAR = 'q'
IS_POPUP_VAR = 'pop' IS_POPUP_VAR = 'pop'
ERROR_FLAG = 'e'
# Text to display within change-list table cells if the value is blank. # Text to display within change-list table cells if the value is blank.
EMPTY_CHANGELIST_VALUE = '(None)' EMPTY_CHANGELIST_VALUE = '(None)'
@ -73,8 +74,7 @@ def unquote(s):
for item in list: for item in list:
if item[1:2]: if item[1:2]:
try: try:
myappend(mychr(myatoi(item[:2], 16)) myappend(mychr(myatoi(item[:2], 16)) + item[2:])
+ item[2:])
except ValueError: except ValueError:
myappend('_' + item) myappend('_' + item)
else: else:
@ -143,9 +143,9 @@ class AdminBoundField(object):
return self._display return self._display
except AttributeError: except AttributeError:
if isinstance(self.field.rel, models.ManyToOneRel): if isinstance(self.field.rel, models.ManyToOneRel):
self._display = getattr(self.original, self.field.attname) self._display = getattr(self.original, self.field.name)
elif isinstance(self.field.rel, models.ManyToManyRel): elif isinstance(self.field.rel, models.ManyToManyRel):
self._display = ", ".join([str(obj) for obj in getattr(self.original, self.field.attname).all()]) self._display = ", ".join([str(obj) for obj in getattr(self.original, self.field.name).all()])
return self._display return self._display
def __repr__(self): def __repr__(self):
@ -557,6 +557,8 @@ class ChangeList(object):
self.params = dict(request.GET.items()) self.params = dict(request.GET.items())
if self.params.has_key(PAGE_VAR): if self.params.has_key(PAGE_VAR):
del self.params[PAGE_VAR] del self.params[PAGE_VAR]
if self.params.has_key(ERROR_FLAG):
del self.params[ERROR_FLAG]
self.order_field, self.order_type = self.get_ordering() self.order_field, self.order_type = self.get_ordering()
self.query = request.GET.get(SEARCH_VAR, '') self.query = request.GET.get(SEARCH_VAR, '')
@ -730,7 +732,14 @@ def change_list(request, app_label, model_name):
try: try:
cl = ChangeList(request, model) cl = ChangeList(request, model)
except IncorrectLookupParameters: except IncorrectLookupParameters:
return HttpResponseRedirect(request.path) # Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
# parameter via the query string. If wacky parameters were given and
# the 'invalid=1' parameter was already in the query string, something
# is screwed up with the database, so display an error page.
if ERROR_FLAG in request.GET.keys():
return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
c = template.RequestContext(request, { c = template.RequestContext(request, {
'title': cl.title, 'title': cl.title,
'is_popup': cl.is_popup, 'is_popup': cl.is_popup,

View File

@ -10,8 +10,6 @@ def authenhandler(req, **kwargs):
# that so that the following import works # that so that the following import works
os.environ.update(req.subprocess_env) os.environ.update(req.subprocess_env)
from django.contrib.auth.models import User
# check for PythonOptions # check for PythonOptions
_str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes')
@ -19,6 +17,11 @@ def authenhandler(req, **kwargs):
permission_name = options.get('DjangoPermissionName', None) permission_name = options.get('DjangoPermissionName', None)
staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on")) staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on"))
superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off")) superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off"))
settings_module = options.get('DJANGO_SETTINGS_MODULE', None)
if settings_module:
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
from django.contrib.auth.models import User
# check that the username is valid # check that the username is valid
kwargs = {'username': req.user, 'is_active': True} kwargs = {'username': req.user, 'is_active': True}

View File

@ -32,7 +32,7 @@ class Permission(models.Model):
ordering = ('content_type', 'codename') ordering = ('content_type', 'codename')
def __str__(self): def __str__(self):
return "%r | %s" % (self.content_type, self.name) return "%s | %s" % (self.content_type, self.name)
class Group(models.Model): class Group(models.Model):
name = models.CharField(_('name'), maxlength=80, unique=True) name = models.CharField(_('name'), maxlength=80, unique=True)

View File

@ -79,9 +79,14 @@ class SessionMiddleware:
else: else:
if modified or settings.SESSION_SAVE_EVERY_REQUEST: if modified or settings.SESSION_SAVE_EVERY_REQUEST:
session_key = request.session.session_key or Session.objects.get_new_session_key() session_key = request.session.session_key or Session.objects.get_new_session_key()
if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
max_age = None
expires = None
else:
max_age = settings.SESSION_COOKIE_AGE
expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT")
new_session = Session.objects.save(session_key, request.session._session, new_session = Session.objects.save(session_key, request.session._session,
datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)) datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT")
response.set_cookie(settings.SESSION_COOKIE_NAME, session_key, response.set_cookie(settings.SESSION_COOKIE_NAME, session_key,
max_age=settings.SESSION_COOKIE_AGE, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN) max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN)
return response return response

View File

@ -334,8 +334,8 @@ def get_sql_initial_data_for_model(model):
r"""( # each statement is... r"""( # each statement is...
(?: # one or more chunks of ... (?: # one or more chunks of ...
(?:[^;'"]+) # not the end of a statement or start of a quote (?:[^;'"]+) # not the end of a statement or start of a quote
| (?:'[^']+') # something in single quotes | (?:'[^']*') # something in single quotes
| (?:"[^"]+") # something in double quotes | (?:"[^"]*") # something in double quotes
)+)""", re.VERBOSE) )+)""", re.VERBOSE)
# Find custom SQL, if it's available. # Find custom SQL, if it's available.

View File

@ -54,6 +54,26 @@ class ObjectPaginator:
def has_previous_page(self, page_number): def has_previous_page(self, page_number):
return page_number > 0 return page_number > 0
def first_on_page(self, page_number):
"""
Returns the 1-based index of the first object on the given page,
relative to total objects found (hits).
"""
if page_number == 0:
return 1
return (self.num_per_page * page_number) + 1
def last_on_page(self, page_number):
"""
Returns the 1-based index of the last object on the given page,
relative to total objects found (hits).
"""
if page_number == 0 and self.num_per_page >= self._hits:
return self._hits
elif page_number == (self._pages - 1) and (page_number + 1) * self.num_per_page > self._hits:
return self._hits
return (page_number + 1) * self.num_per_page
def _get_hits(self): def _get_hits(self):
if self._hits is None: if self._hits is None:
self._hits = self.query_set.count() self._hits = self.query_set.count()

View File

@ -20,7 +20,10 @@ alnumurl_re = re.compile(r'^[-\w/]+$')
ansi_date_re = re.compile('^%s$' % _datere) ansi_date_re = re.compile('^%s$' % _datere)
ansi_time_re = re.compile('^%s$' % _timere) ansi_time_re = re.compile('^%s$' % _timere)
ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere)) ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere))
email_re = re.compile(r'^[A-Z0-9._%-][+A-Z0-9._%-]*@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$', re.IGNORECASE) email_re = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$', re.IGNORECASE) # domain
integer_re = re.compile(r'^-?\d+$') integer_re = re.compile(r'^-?\d+$')
ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE)
@ -143,7 +146,11 @@ def isValidImage(field_data, all_data):
from PIL import Image from PIL import Image
from cStringIO import StringIO from cStringIO import StringIO
try: try:
Image.open(StringIO(field_data['content'])) content = field_data['content']
except TypeError:
raise ValidationError, gettext("No file was submitted. Check the encoding type on the form.")
try:
Image.open(StringIO(content))
except IOError: # Python Imaging Library doesn't recognize it as an image except IOError: # Python Imaging Library doesn't recognize it as an image
raise ValidationError, gettext("Upload a valid image. The file you uploaded was either not an image or a corrupted image.") raise ValidationError, gettext("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
@ -363,9 +370,13 @@ class HasAllowableSize:
self.max_error_message = max_error_message or lazy_inter(gettext_lazy("Make sure your uploaded file is at most %s bytes big."), max_size) self.max_error_message = max_error_message or lazy_inter(gettext_lazy("Make sure your uploaded file is at most %s bytes big."), max_size)
def __call__(self, field_data, all_data): def __call__(self, field_data, all_data):
if self.min_size is not None and len(field_data['content']) < self.min_size: try:
content = field_data['content']
except TypeError:
raise ValidationError, gettext_lazy("No file was submitted. Check the encoding type on the form.")
if self.min_size is not None and len(content) < self.min_size:
raise ValidationError, self.min_error_message raise ValidationError, self.min_error_message
if self.max_size is not None and len(field_data['content']) > self.max_size: if self.max_size is not None and len(content) > self.max_size:
raise ValidationError, self.max_error_message raise ValidationError, self.max_error_message
class MatchesRegularExpression: class MatchesRegularExpression:

View File

@ -45,27 +45,26 @@ def get_indexes(cursor, table_name):
{'primary_key': boolean representing whether it's the primary key, {'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index} 'unique': boolean representing whether it's a unique index}
""" """
# Get the table description because we only have the column indexes, and we # This query retrieves each index on the given table, including the
# need the column names. # first associated field name
desc = get_table_description(cursor, table_name)
# This query retrieves each index on the given table.
cursor.execute(""" cursor.execute("""
SELECT idx.indkey, idx.indisunique, idx.indisprimary SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
pg_catalog.pg_index idx pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
WHERE c.oid = idx.indrelid WHERE c.oid = idx.indrelid
AND idx.indexrelid = c2.oid AND idx.indexrelid = c2.oid
AND attr.attrelid = c.oid
AND attr.attnum = idx.indkey[0]
AND c.relname = %s""", [table_name]) AND c.relname = %s""", [table_name])
indexes = {} indexes = {}
for row in cursor.fetchall(): for row in cursor.fetchall():
# row[0] (idx.indkey) is stored in the DB as an array. It comes out as # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
# a string of space-separated integers. This designates the field # a string of space-separated integers. This designates the field
# indexes (1-based) of the fields that have indexes on the table. # indexes (1-based) of the fields that have indexes on the table.
# Here, we skip any indexes across multiple fields. # Here, we skip any indexes across multiple fields.
if ' ' in row[0]: if ' ' in row[1]:
continue continue
col_name = desc[int(row[0])-1][0] indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
indexes[col_name] = {'primary_key': row[2], 'unique': row[1]}
return indexes return indexes
# Maps type codes to Django Field types. # Maps type codes to Django Field types.

View File

@ -45,27 +45,26 @@ def get_indexes(cursor, table_name):
{'primary_key': boolean representing whether it's the primary key, {'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index} 'unique': boolean representing whether it's a unique index}
""" """
# Get the table description because we only have the column indexes, and we # This query retrieves each index on the given table, including the
# need the column names. # first associated field name
desc = get_table_description(cursor, table_name)
# This query retrieves each index on the given table.
cursor.execute(""" cursor.execute("""
SELECT idx.indkey, idx.indisunique, idx.indisprimary SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
pg_catalog.pg_index idx pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
WHERE c.oid = idx.indrelid WHERE c.oid = idx.indrelid
AND idx.indexrelid = c2.oid AND idx.indexrelid = c2.oid
AND attr.attrelid = c.oid
AND attr.attnum = idx.indkey[0]
AND c.relname = %s""", [table_name]) AND c.relname = %s""", [table_name])
indexes = {} indexes = {}
for row in cursor.fetchall(): for row in cursor.fetchall():
# row[0] (idx.indkey) is stored in the DB as an array. It comes out as # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
# a string of space-separated integers. This designates the field # a string of space-separated integers. This designates the field
# indexes (1-based) of the fields that have indexes on the table. # indexes (1-based) of the fields that have indexes on the table.
# Here, we skip any indexes across multiple fields. # Here, we skip any indexes across multiple fields.
if ' ' in row[0]: if ' ' in row[1]:
continue continue
col_name = desc[int(row[0])-1][0] indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
indexes[col_name] = {'primary_key': row[2], 'unique': row[1]}
return indexes return indexes
# Maps type codes to Django Field types. # Maps type codes to Django Field types.

View File

@ -12,6 +12,10 @@ class CursorDebugWrapper:
return self.cursor.execute(sql, params) return self.cursor.execute(sql, params)
finally: finally:
stop = time() stop = time()
# If params was a list, convert it to a tuple, because string
# formatting with '%' only works with tuples or dicts.
if not isinstance(params, (tuple, dict)):
params = tuple(params)
self.db.queries.append({ self.db.queries.append({
'sql': sql % tuple(params), 'sql': sql % tuple(params),
'time': "%.3f" % (stop - start), 'time': "%.3f" % (stop - start),

View File

@ -161,7 +161,7 @@ class Model(object):
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
# If it does already exist, do an UPDATE. # If it does already exist, do an UPDATE.
if cursor.fetchone(): if cursor.fetchone():
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks] db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks]
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
(backend.quote_name(self._meta.db_table), (backend.quote_name(self._meta.db_table),
','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]),
@ -171,11 +171,11 @@ class Model(object):
record_exists = False record_exists = False
if not pk_set or not record_exists: if not pk_set or not record_exists:
field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in self._meta.fields if not isinstance(f, AutoField)] db_values = [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
# If the PK has been manually set, respect that. # If the PK has been manually set, respect that.
if pk_set: if pk_set:
field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
db_values += [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in self._meta.fields if isinstance(f, AutoField)] db_values += [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
placeholders = ['%s'] * len(field_names) placeholders = ['%s'] * len(field_names)
if self._meta.order_with_respect_to: if self._meta.order_with_respect_to:
field_names.append(backend.quote_name('_order')) field_names.append(backend.quote_name('_order'))
@ -279,7 +279,7 @@ class Model(object):
order_field = self._meta.order_with_respect_to order_field = self._meta.order_with_respect_to
where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \ where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
(backend.quote_name('_order'), op, backend.quote_name('_order'), (backend.quote_name('_order'), op, backend.quote_name('_order'),
backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)), backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)),
'%s=%%s' % backend.quote_name(order_field.column)] '%s=%%s' % backend.quote_name(order_field.column)]
params = [self._get_pk_val(), getattr(self, order_field.attname)] params = [self._get_pk_val(), getattr(self, order_field.attname)]
obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get() obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get()

View File

@ -152,9 +152,9 @@ class Field(object):
def get_internal_type(self): def get_internal_type(self):
return self.__class__.__name__ return self.__class__.__name__
def pre_save(self, value, add): def pre_save(self, model_instance, add):
"Returns field's value just before saving." "Returns field's value just before saving."
return value return getattr(model_instance, self.attname)
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
"Returns field's value prepared for saving into a database." "Returns field's value prepared for saving into a database."
@ -417,10 +417,13 @@ class DateField(Field):
value = str(value) value = str(value)
return Field.get_db_prep_lookup(self, lookup_type, value) return Field.get_db_prep_lookup(self, lookup_type, value)
def pre_save(self, value, add): def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add): if self.auto_now or (self.auto_now_add and add):
return datetime.datetime.now() value = datetime.datetime.now()
setattr(model_instance, self.attname, value)
return value return value
else:
return super(DateField, self).pre_save(model_instance, add)
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
super(DateField,self).contribute_to_class(cls, name) super(DateField,self).contribute_to_class(cls, name)
@ -723,10 +726,13 @@ class TimeField(Field):
value = str(value) value = str(value)
return Field.get_db_prep_lookup(self, lookup_type, value) return Field.get_db_prep_lookup(self, lookup_type, value)
def pre_save(self, value, add): def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add): if self.auto_now or (self.auto_now_add and add):
return datetime.datetime.now().time() value = datetime.datetime.now().time()
setattr(model_instance, self.attname, value)
return value return value
else:
return super(TimeField, self).pre_save(model_instance, add)
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
# Casts dates into string format for entry into database. # Casts dates into string format for entry into database.

View File

@ -3,8 +3,8 @@ from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models import signals from django.db.models import signals
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
import operator import operator
import re
# For Python 2.3 # For Python 2.3
if not hasattr(__builtins__, 'set'): if not hasattr(__builtins__, 'set'):
@ -59,7 +59,7 @@ def orderlist2sql(order_list, opts, prefix=''):
return ', '.join(output) return ', '.join(output)
def quote_only_if_word(word): def quote_only_if_word(word):
if ' ' in word: if re.search('\W', word): # Don't quote if there are spaces or non-word chars.
return word return word
else: else:
return backend.quote_name(word) return backend.quote_name(word)
@ -380,6 +380,10 @@ class QuerySet(object):
# (so that A.filter(args1) & A.filter(args2) does the same as # (so that A.filter(args1) & A.filter(args2) does the same as
# A.filter(args1).filter(args2) # A.filter(args1).filter(args2)
combined = other._clone() combined = other._clone()
if self._select: combined._select.update(self._select)
if self._where: combined._where.extend(self._where)
if self._params: combined._params.extend(self._params)
if self._tables: combined._tables.extend(self._tables)
# If 'self' is ordered and 'other' isn't, propagate 'self's ordering # If 'self' is ordered and 'other' isn't, propagate 'self's ordering
if (self._order_by is not None and len(self._order_by) > 0) and \ if (self._order_by is not None and len(self._order_by) > 0) and \
(combined._order_by is None or len(combined._order_by) == 0): (combined._order_by is None or len(combined._order_by) == 0):

View File

@ -12,7 +12,10 @@ Managed transactions don't do those commits, but will need some kind of manual
or implicit commits or rollbacks. or implicit commits or rollbacks.
""" """
import thread try:
import thread
except ImportError:
import dummy_thread as thread
from django.db import connection from django.db import connection
from django.conf import settings from django.conf import settings

View File

@ -577,7 +577,7 @@ class SelectMultipleField(SelectField):
selected_html = '' selected_html = ''
if str(value) in str_data_list: if str(value) in str_data_list:
selected_html = ' selected="selected"' selected_html = ' selected="selected"'
output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, choice)) output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice)))
output.append(' </select>') output.append(' </select>')
return '\n'.join(output) return '\n'.join(output)
@ -641,7 +641,11 @@ class FileUploadField(FormField):
self.validator_list = [self.isNonEmptyFile] + validator_list self.validator_list = [self.isNonEmptyFile] + validator_list
def isNonEmptyFile(self, field_data, all_data): def isNonEmptyFile(self, field_data, all_data):
if not field_data['content']: try:
content = field_data['content']
except TypeError:
raise validators.CriticalValidationError, gettext("No file was submitted. Check the encoding type on the form.")
if not content:
raise validators.CriticalValidationError, gettext("The submitted file is empty.") raise validators.CriticalValidationError, gettext("The submitted file is empty.")
def render(self, data): def render(self, data):

View File

@ -10,14 +10,14 @@ def render_to_response(*args, **kwargs):
return HttpResponse(loader.render_to_string(*args, **kwargs)) return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility. load_and_render = render_to_response # For backwards compatibility.
def get_object_or_404(klass, **kwargs): def get_object_or_404(klass, *args, **kwargs):
try: try:
return klass._default_manager.get(**kwargs) return klass._default_manager.get(*args, **kwargs)
except klass.DoesNotExist: except klass.DoesNotExist:
raise Http404 raise Http404
def get_list_or_404(klass, **kwargs): def get_list_or_404(klass, *args, **kwargs):
obj_list = list(klass._default_manager.filter(**kwargs)) obj_list = list(klass._default_manager.filter(*args, **kwargs))
if not obj_list: if not obj_list:
raise Http404 raise Http404
return obj_list return obj_list

View File

@ -234,4 +234,7 @@ class local(_localbase):
return __del__ return __del__
__del__ = __del__() __del__ = __del__()
from threading import currentThread, enumerate, RLock try:
from threading import currentThread, enumerate, RLock
except ImportError:
from dummy_threading import currentThread, enumerate, RLock

View File

@ -28,7 +28,12 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os, sys, thread, time import os, sys, time
try:
import thread
except ImportError:
import dummy_thread as thread
RUN_RELOADER = True RUN_RELOADER = True

View File

@ -217,7 +217,7 @@ class Atom1Feed(SyndicationFeed):
for item in self.items: for item in self.items:
handler.startElement(u"entry", {}) handler.startElement(u"entry", {})
handler.addQuickElement(u"title", item['title']) handler.addQuickElement(u"title", item['title'])
handler.addQuickElement(u"link", u"", {u"href": item['link']}) handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"})
if item['pubdate'] is not None: if item['pubdate'] is not None:
handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii')) handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii'))

View File

@ -6,7 +6,8 @@ import datetime, time
def archive_index(request, queryset, date_field, num_latest=15, def archive_index(request, queryset, date_field, num_latest=15,
template_name=None, template_loader=loader, template_name=None, template_loader=loader,
extra_context={}, allow_empty=False, context_processors=None): extra_context={}, allow_empty=False, context_processors=None,
mimetype=None):
""" """
Generic top-level archive of date-based objects. Generic top-level archive of date-based objects.
@ -40,11 +41,12 @@ def archive_index(request, queryset, date_field, num_latest=15,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def archive_year(request, year, queryset, date_field, template_name=None, def archive_year(request, year, queryset, date_field, template_name=None,
template_loader=loader, extra_context={}, allow_empty=False, template_loader=loader, extra_context={}, allow_empty=False,
context_processors=None): context_processors=None, template_object_name='object', mimetype=None,
make_object_list=False):
""" """
Generic yearly archive view. Generic yearly archive view.
@ -54,6 +56,9 @@ def archive_year(request, year, queryset, date_field, template_name=None,
List of months in this year with objects List of months in this year with objects
year year
This year This year
object_list
List of objects published in the given month
(Only available if make_object_list argument is True)
""" """
model = queryset.model model = queryset.model
now = datetime.datetime.now() now = datetime.datetime.now()
@ -66,24 +71,29 @@ def archive_year(request, year, queryset, date_field, template_name=None,
date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month') date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month')
if not date_list and not allow_empty: if not date_list and not allow_empty:
raise Http404 raise Http404
if make_object_list:
object_list = queryset.filter(**lookup_kwargs).order_by(date_field)
else:
object_list = []
if not template_name: if not template_name:
template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name) t = template_loader.get_template(template_name)
c = RequestContext(request, { c = RequestContext(request, {
'date_list': date_list, 'date_list': date_list,
'year': year, 'year': year,
'%s_list' % template_object_name: object_list,
}, context_processors) }, context_processors)
for key, value in extra_context.items(): for key, value in extra_context.items():
if callable(value): if callable(value):
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def archive_month(request, year, month, queryset, date_field, def archive_month(request, year, month, queryset, date_field,
month_format='%b', template_name=None, template_loader=loader, month_format='%b', template_name=None, template_loader=loader,
extra_context={}, allow_empty=False, context_processors=None, extra_context={}, allow_empty=False, context_processors=None,
template_object_name='object'): template_object_name='object', mimetype=None):
""" """
Generic monthly archive view. Generic monthly archive view.
@ -134,12 +144,12 @@ def archive_month(request, year, month, queryset, date_field,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def archive_week(request, year, week, queryset, date_field, def archive_week(request, year, week, queryset, date_field,
template_name=None, template_loader=loader, template_name=None, template_loader=loader,
extra_context={}, allow_empty=True, context_processors=None, extra_context={}, allow_empty=True, context_processors=None,
template_object_name='object'): template_object_name='object', mimetype=None):
""" """
Generic weekly archive view. Generic weekly archive view.
@ -181,12 +191,13 @@ def archive_week(request, year, week, queryset, date_field,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def archive_day(request, year, month, day, queryset, date_field, def archive_day(request, year, month, day, queryset, date_field,
month_format='%b', day_format='%d', template_name=None, month_format='%b', day_format='%d', template_name=None,
template_loader=loader, extra_context={}, allow_empty=False, template_loader=loader, extra_context={}, allow_empty=False,
context_processors=None, template_object_name='object'): context_processors=None, template_object_name='object',
mimetype=None):
""" """
Generic daily archive view. Generic daily archive view.
@ -233,7 +244,7 @@ def archive_day(request, year, month, day, queryset, date_field,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def archive_today(request, **kwargs): def archive_today(request, **kwargs):
""" """
@ -251,7 +262,7 @@ def object_detail(request, year, month, day, queryset, date_field,
month_format='%b', day_format='%d', object_id=None, slug=None, month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None, slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_context={}, context_processors=None, template_loader=loader, extra_context={}, context_processors=None,
template_object_name='object'): template_object_name='object', mimetype=None):
""" """
Generic detail view from year/month/day/slug or year/month/day/id structure. Generic detail view from year/month/day/slug or year/month/day/id structure.
@ -300,6 +311,6 @@ def object_detail(request, year, month, day, queryset, date_field,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
response = HttpResponse(t.render(c)) response = HttpResponse(t.render(c), mimetype=mimetype)
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name)) populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
return response return response

View File

@ -6,7 +6,8 @@ from django.core.exceptions import ObjectDoesNotExist
def object_list(request, queryset, paginate_by=None, allow_empty=False, def object_list(request, queryset, paginate_by=None, allow_empty=False,
template_name=None, template_loader=loader, template_name=None, template_loader=loader,
extra_context={}, context_processors=None, template_object_name='object'): extra_context={}, context_processors=None, template_object_name='object',
mimetype=None):
""" """
Generic list of objects. Generic list of objects.
@ -73,12 +74,13 @@ def object_list(request, queryset, paginate_by=None, allow_empty=False,
model = queryset.model model = queryset.model
template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name) t = template_loader.get_template(template_name)
return HttpResponse(t.render(c)) return HttpResponse(t.render(c), mimetype=mimetype)
def object_detail(request, queryset, object_id=None, slug=None, def object_detail(request, queryset, object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None, slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_context={}, template_loader=loader, extra_context={},
context_processors=None, template_object_name='object'): context_processors=None, template_object_name='object',
mimetype=None):
""" """
Generic list of objects. Generic list of objects.
@ -113,6 +115,6 @@ def object_detail(request, queryset, object_id=None, slug=None,
c[key] = value() c[key] = value()
else: else:
c[key] = value c[key] = value
response = HttpResponse(t.render(c)) response = HttpResponse(t.render(c), mimetype=mimetype)
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name)) populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
return response return response

View File

@ -56,6 +56,15 @@ location to users marked as staff members. You can use a set of
required. required.
================================ ========================================= ================================ =========================================
Note that sometimes ``SetEnv`` doesn't play well in this mod_python
configuration, for reasons unknown. If you're having problems getting
mod_python to recognize your ``DJANGO_SETTINGS_MODULE``, you can set it using
``PythonOption`` instead of ``SetEnv``. Therefore, these two Apache directives
are equivalent::
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
.. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _authentication system: http://www.djangoproject.com/documentation/authentication/
.. _Subversion: http://subversion.tigris.org/ .. _Subversion: http://subversion.tigris.org/
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html .. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html

View File

@ -182,6 +182,9 @@ a date in the *future* are not included.
* ``context_processors``: A list of template-context processors to apply to * ``context_processors``: A list of template-context processors to apply to
the view's template. See the `RequestContext docs`_. the view's template. See the `RequestContext docs`_.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -247,6 +250,21 @@ with a date in the *future* are not displayed.
* ``context_processors``: A list of template-context processors to apply to * ``context_processors``: A list of template-context processors to apply to
the view's template. See the `RequestContext docs`_. the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The
view will append ``'_list'`` to the value of this parameter in
determining the variable's name.
* ``make_object_list``: A boolean specifying whether to retrieve the full
list of objects for this year and pass those to the template. If ``True``,
this list of objects will be made available to the template as
``object_list``. (The name ``object_list`` may be different; see the docs
for ``object_list`` in the "Template context" section below.) By default,
this is ``False``.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -259,8 +277,19 @@ In addition to ``extra_context``, the template's context will be:
* ``date_list``: A list of ``datetime.date`` objects representing all * ``date_list``: A list of ``datetime.date`` objects representing all
months that have objects available in the given year, according to months that have objects available in the given year, according to
``queryset``, in ascending order. ``queryset``, in ascending order.
* ``year``: The given year, as a four-character string. * ``year``: The given year, as a four-character string.
* ``object_list``: If the ``make_object_list`` parameter is ``True``, this
will be set to a list of objects available for the given year, ordered by
the date field. This variable's name depends on the
``template_object_name`` parameter, which is ``'object'`` by default. If
``template_object_name`` is ``'foo'``, this variable's name will be
``foo_list``.
If ``make_object_list`` is ``False``, ``object_list`` will be passed to
the template as an empty list.
``django.views.generic.date_based.archive_month`` ``django.views.generic.date_based.archive_month``
------------------------------------------------- -------------------------------------------------
@ -314,6 +343,9 @@ date in the *future* are not displayed.
view will append ``'_list'`` to the value of this parameter in view will append ``'_list'`` to the value of this parameter in
determining the variable's name. determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -387,6 +419,9 @@ in the *future* are not displayed.
view will append ``'_list'`` to the value of this parameter in view will append ``'_list'`` to the value of this parameter in
determining the variable's name. determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -463,6 +498,9 @@ a 404 error, regardless of whether any objects exist for future days.
view will append ``'_list'`` to the value of this parameter in view will append ``'_list'`` to the value of this parameter in
determining the variable's name. determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -563,6 +601,9 @@ A page representing an individual object.
* ``template_object_name``: Designates the name of the template variable * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. to use in the template context. By default, this is ``'object'``.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -627,6 +668,9 @@ A page representing a list of objects.
view will append ``'_list'`` to the value of this parameter in view will append ``'_list'`` to the value of this parameter in
determining the variable's name. determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template
@ -717,6 +761,9 @@ A page representing an individual object.
* ``template_object_name``: Designates the name of the template variable * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. to use in the template context. By default, this is ``'object'``.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting.
**Template name:** **Template name:**
If ``template_name`` isn't specified, this view will use the template If ``template_name`` isn't specified, this view will use the template

View File

@ -162,11 +162,15 @@ A date field. Has a few extra optional arguments:
====================== =================================================== ====================== ===================================================
``auto_now`` Automatically set the field to now every time the ``auto_now`` Automatically set the field to now every time the
object is saved. Useful for "last-modified" object is saved. Useful for "last-modified"
timestamps. timestamps. Note that the current date is *always*
used; it's not just a default value that you can
override.
``auto_now_add`` Automatically set the field to now when the object ``auto_now_add`` Automatically set the field to now when the object
is first created. Useful for creation of is first created. Useful for creation of
timestamps. timestamps. Note that the current date is *always*
used; it's not just a default value that you can
override.
====================== =================================================== ====================== ===================================================
The admin represents this as an ``<input type="text">`` with a JavaScript The admin represents this as an ``<input type="text">`` with a JavaScript
@ -488,6 +492,10 @@ cleared, the object will be deleted.
It is an error to have an inline-editable relation without at least one It is an error to have an inline-editable relation without at least one
``core=True`` field. ``core=True`` field.
Please note that each field marked "core" is treated as a required field by the
Django admin site. Essentially, this means you should put ``core=True`` on all
required fields in your related object that is being edited inline.
``db_column`` ``db_column``
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -197,6 +197,22 @@ will be sent on every request.
Similarly, the ``expires`` part of a session cookie is updated each time the Similarly, the ``expires`` part of a session cookie is updated each time the
session cookie is sent. session cookie is sent.
Browser-length sessions vs. persistent sessions
===============================================
You can control whether the session framework uses browser-length sessions vs.
persistent sessions with the ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` setting.
By default, ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``False``, which
means session cookies will be stored in users' browsers for as long as
``SESSION_COOKIE_AGE``. Use this if you don't want people to have to log in
every time they open a browser.
If ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``True``, Django will use
browser-length cookies -- cookies that expire as soon as the user closes his or
her browser. Use this if you want people to have to log in every time they open
a browser.
Settings Settings
======== ========
@ -225,6 +241,14 @@ Default: ``'sessionid'``
The name of the cookie to use for sessions. This can be whatever you want. The name of the cookie to use for sessions. This can be whatever you want.
SESSION_EXPIRE_AT_BROWSER_CLOSE
-------------------------------
Default: ``False``
Whether to expire the session when the user closes his or her browser. See
"Browser-length sessions vs. persistent sessions" above.
SESSION_SAVE_EVERY_REQUEST SESSION_SAVE_EVERY_REQUEST
-------------------------- --------------------------

View File

@ -603,6 +603,14 @@ Default: ``'sessionid'``
The name of the cookie to use for sessions. This can be whatever you want. The name of the cookie to use for sessions. This can be whatever you want.
See the `session docs`_. See the `session docs`_.
SESSION_EXPIRE_AT_BROWSER_CLOSE
-------------------------------
Default: ``False``
Whether to expire the session when the user closes his or her browser.
See the `session docs`_.
SESSION_SAVE_EVERY_REQUEST SESSION_SAVE_EVERY_REQUEST
-------------------------- --------------------------

View File

@ -85,7 +85,7 @@ creating an empty class means "give this object an admin interface using
all the default options." all the default options."
Now reload the Django admin page to see your changes. Note that you don't have Now reload the Django admin page to see your changes. Note that you don't have
to restart the development server -- the server will auto-reloads your project, to restart the development server -- the server will auto-reload your project,
so any modifications code will be seen immediately in your browser. so any modifications code will be seen immediately in your browser.
Explore the free admin functionality Explore the free admin functionality

View File

@ -209,13 +209,13 @@ So let's use Django's template system to separate the design from Python::
}) })
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
That code loads the template called "polls/index" and passes it a context. The That code loads the template called "polls/index.html" and passes it a context. The
context is a dictionary mapping template variable names to Python objects. context is a dictionary mapping template variable names to Python objects.
Reload the page. Now you'll see an error:: Reload the page. Now you'll see an error::
TemplateDoesNotExist: Your TEMPLATE_DIRS settings is empty. TemplateDoesNotExist at /polls/
Change it to point to at least one template directory. polls/index.html
Ah. There's no template yet. First, create a directory, somewhere on your Ah. There's no template yet. First, create a directory, somewhere on your
filesystem, whose contents Django can access. (Django runs as whatever user filesystem, whose contents Django can access. (Django runs as whatever user

View File

@ -1,5 +1,12 @@
""" """
23. Giving models a custom manager 23. Giving models a custom manager
You can use a custom ``Manager`` in a particular model by extending the base
``Manager`` class and instantiating your custom ``Manager`` in your model.
There are two reasons you might want to customize a ``Manager``: to add extra
``Manager`` methods, and/or to modify the initial ``QuerySet`` the ``Manager``
returns.
""" """
from django.db import models from django.db import models
@ -19,7 +26,7 @@ class Person(models.Model):
def __repr__(self): def __repr__(self):
return "%s %s" % (self.first_name, self.last_name) return "%s %s" % (self.first_name, self.last_name)
# An example of a custom manager that sets a core_filter on its lookups. # An example of a custom manager that sets get_query_set().
class PublishedBookManager(models.Manager): class PublishedBookManager(models.Manager):
def get_query_set(self): def get_query_set(self):

View File

@ -1,5 +1,5 @@
""" """
3. Giving models custom methods and custom managers 3. Giving models custom methods
Any method you add to a model will be available to instances. Any method you add to a model will be available to instances.
""" """

View File

@ -1,7 +1,12 @@
""" """
XXX. Callable defaults 31. Callable defaults
??? You can pass callable objects as the ``default`` parameter to a field. When
the object is created without an explicit value passed in, Django will call
the method to determine the default value.
This example uses ``datetime.datetime.now`` as the default for the ``pub_date``
field.
""" """
from django.db import models from django.db import models
@ -9,9 +14,9 @@ from datetime import datetime
class Article(models.Model): class Article(models.Model):
headline = models.CharField(maxlength=100, default='Default headline') headline = models.CharField(maxlength=100, default='Default headline')
pub_date = models.DateTimeField(default = datetime.now) pub_date = models.DateTimeField(default=datetime.now)
def __repr__(self): def __str__(self):
return self.headline return self.headline
API_TESTS = """ API_TESTS = """
@ -43,6 +48,4 @@ API_TESTS = """
>>> d = now - a.pub_date >>> d = now - a.pub_date
>>> d.seconds < 5 >>> d.seconds < 5
True True
""" """

View File

@ -1,5 +1,7 @@
""" """
26. A test to check that the model validator works can correctly identify errors in a model. 26. Invalid models
This example exists purely to point out errors in models.
""" """
from django.db import models from django.db import models

View File

@ -1,8 +1,7 @@
""" """
27. Many-to-many and many-to-one relationships to the same table. 28. Many-to-many and many-to-one relationships to the same table
This is a response to bug #1535
Make sure to set ``related_name`` if you use relationships to the same table.
""" """
from django.db import models from django.db import models
@ -14,8 +13,9 @@ class Issue(models.Model):
num = models.IntegerField() num = models.IntegerField()
cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc') cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc')
client = models.ForeignKey(User, related_name='test_issue_client') client = models.ForeignKey(User, related_name='test_issue_client')
def __repr__(self):
return "<Issue %d>" % (self.num,) def __str__(self):
return str(self.num)
class Meta: class Meta:
ordering = ('num',) ordering = ('num',)
@ -47,20 +47,20 @@ API_TESTS = """
>>> i3.cc.add(r) >>> i3.cc.add(r)
>>> from django.db.models.query import Q >>> from django.db.models.query import Q
>>> Issue.objects.filter(client=r.id) >>> Issue.objects.filter(client=r.id)
[<Issue 1>, <Issue 2>] [<Issue: 1>, <Issue: 2>]
>>> Issue.objects.filter(client=g.id) >>> Issue.objects.filter(client=g.id)
[<Issue 3>] [<Issue: 3>]
>>> Issue.objects.filter(cc__id__exact=g.id) >>> Issue.objects.filter(cc__id__exact=g.id)
[] []
>>> Issue.objects.filter(cc__id__exact=r.id) >>> Issue.objects.filter(cc__id__exact=r.id)
[<Issue 2>, <Issue 3>] [<Issue: 2>, <Issue: 3>]
# Queries that combine results from the m2m and the m2o relationship. # Queries that combine results from the m2m and the m2o relationship.
# 3 ways of saying the same thing: # 3 ways of saying the same thing:
>>> Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id)) >>> Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id))
[<Issue 1>, <Issue 2>, <Issue 3>] [<Issue: 1>, <Issue: 2>, <Issue: 3>]
>>> Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id) >>> Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id)
[<Issue 1>, <Issue 2>, <Issue 3>] [<Issue: 1>, <Issue: 2>, <Issue: 3>]
>>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)) >>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id))
[<Issue 1>, <Issue 2>, <Issue 3>] [<Issue: 1>, <Issue: 2>, <Issue: 3>]
""" """

View File

@ -1,5 +1,5 @@
""" """
26. Many-to-many relationships between the same two tables 27. Many-to-many relationships between the same two tables
In this example, A Person can have many friends, who are also people. Friendship is a In this example, A Person can have many friends, who are also people. Friendship is a
symmetrical relationshiup - if I am your friend, you are my friend. symmetrical relationshiup - if I am your friend, you are my friend.

View File

@ -1,5 +1,7 @@
""" """
25. Default manipulators 26. Default manipulators
Each model gets an AddManipulator and ChangeManipulator by default.
""" """
from django.db import models from django.db import models

View File

@ -1,6 +1,7 @@
""" """
XX. Model inheritance XX. Model inheritance
Model inheritance isn't yet supported.
""" """
from django.db import models from django.db import models

View File

@ -1,12 +1,11 @@
""" """
20. Object Pagination 29. Object pagination
Django provides a framework for paginating a list of objects in a few.
This is often useful for dividing search results or long lists of objects
in to easily readable pages.
Django provides a framework for paginating a list of objects in a few lines
of code. This is often useful for dividing search results or long lists of
objects into easily readable pages.
""" """
from django.db import models from django.db import models
class Article(models.Model): class Article(models.Model):
@ -56,4 +55,13 @@ False
>>> paginator.has_previous_page(1) >>> paginator.has_previous_page(1)
True True
>>> paginator.first_on_page(0)
1
>>> paginator.first_on_page(1)
6
>>> paginator.last_on_page(0)
5
>>> paginator.last_on_page(1)
9
""" """

View File

@ -1,5 +1,7 @@
""" """
22. Using properties on models 22. Using properties on models
Use properties on models just like on any other Python object.
""" """
from django.db import models from django.db import models

View File

@ -1,5 +1,5 @@
""" """
XXX. Transactions 15. Transactions
Django handles transactions in three different ways. The default is to commit Django handles transactions in three different ways. The default is to commit
each transaction upon a write, but you can decorate a function to get each transaction upon a write, but you can decorate a function to get

View File

@ -1,5 +1,7 @@
""" """
XX. Validation 30. Validation
This is an experimental feature!
Each model instance has a validate() method that returns a dictionary of Each model instance has a validate() method that returns a dictionary of
validation errors in the instance's fields. This method has a side effect validation errors in the instance's fields. This method has a side effect