1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

[multi-db] Merge trunk to [3501]

git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@3502 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jason Pellerin 2006-07-31 18:19:15 +00:00
parent e550c1eed4
commit 7062c656c3
41 changed files with 907 additions and 530 deletions

16
AUTHORS
View File

@ -16,12 +16,23 @@ before Simon departed and currently oversees things with Adrian.
Wilson Miner <http://www.wilsonminer.com/>, who designed Django's admin Wilson Miner <http://www.wilsonminer.com/>, who designed Django's admin
interface, pretty error pages, official Web site (djangoproject.com) and has interface, pretty error pages, official Web site (djangoproject.com) and has
made many other contributions. made many other contributions. He makes us look good.
Malcolm Tredinnick <http://www.pointy-stick.com/blog/>, who has made
significant contributions to all levels of the framework, from its database
layer to template system and documentation.
Georg "Hugo" Bauer <http://hugo.muensterland.org/>, who added Georg "Hugo" Bauer <http://hugo.muensterland.org/>, who added
internationalization support, manages i18n contributions and has made a ton internationalization support, manages i18n contributions and has made a ton
of excellent tweaks, feature additions and bug fixes. of excellent tweaks, feature additions and bug fixes.
Luke Plant <http://lukeplant.me.uk/>, who has contributed many excellent
improvements, including database-level improvements, the CSRF middleware and
unit tests.
Russell Keith-Magee <freakboy@iinet.net.au>, who has contributed many excellent
improvements, including refactoring of the Django ORM code and unit tests.
Robert Wittams <http://robert.wittams.com/>, who majorly refactored the Django Robert Wittams <http://robert.wittams.com/>, who majorly refactored the Django
admin application to allow for easier reuse and has made a ton of excellent admin application to allow for easier reuse and has made a ton of excellent
tweaks, feature additions and bug fixes. tweaks, feature additions and bug fixes.
@ -70,7 +81,6 @@ answer newbie questions, and generally made Django that much better:
Michael Josephson <http://www.sdjournal.com/> Michael Josephson <http://www.sdjournal.com/>
jpellerin@gmail.com jpellerin@gmail.com
junzhang.jn@gmail.com junzhang.jn@gmail.com
Russell Keith-Magee <freakboy@iinet.net.au>
Garth Kidd <http://www.deadlybloodyserious.com/> Garth Kidd <http://www.deadlybloodyserious.com/>
kilian <kilian.cavalotti@lip6.fr> kilian <kilian.cavalotti@lip6.fr>
Sune Kirkeby <http://ibofobi.dk/> Sune Kirkeby <http://ibofobi.dk/>
@ -98,6 +108,7 @@ answer newbie questions, and generally made Django that much better:
Sam Newman <http://www.magpiebrain.com/> Sam Newman <http://www.magpiebrain.com/>
Neal Norwitz <nnorwitz@google.com> Neal Norwitz <nnorwitz@google.com>
oggie rob <oz.robharvey@gmail.com> oggie rob <oz.robharvey@gmail.com>
Jay Parlar <parlar@gmail.com>
pgross@thoughtworks.com pgross@thoughtworks.com
phaedo <http://phaedo.cx/> phaedo <http://phaedo.cx/>
phil@produxion.net phil@produxion.net
@ -118,7 +129,6 @@ answer newbie questions, and generally made Django that much better:
Tom Tobin Tom Tobin
Tom Insam Tom Insam
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/> Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
Malcolm Tredinnick
Amit Upadhyay Amit Upadhyay
Geert Vanderkelen Geert Vanderkelen
Milton Waddams Milton Waddams

19
INSTALL
View File

@ -1,7 +1,22 @@
Thanks for downloading Django. Thanks for downloading Django.
To install it, make sure you have Python 2.3 or greater installed. Then run this command: To install it, make sure you have Python 2.3 or greater installed. Then run
this command from the command prompt:
python setup.py install python setup.py install
Note this requires a working Internet connection if you don't already have the
Python utility "setuptools" installed.
AS AN ALTERNATIVE, you can just copy the entire "django" directory to Python's
site-packages directory, which is located wherever your Python installation
lives. Some places you might check are:
/usr/lib/python2.4/site-packages (Unix, Python 2.4)
/usr/lib/python2.3/site-packages (Unix, Python 2.3)
C:\\PYTHON\site-packages (Windows)
This second solution does not require a working Internet connection; it
bypasses "setuptools" entirely.
For more detailed instructions, see docs/install.txt. For more detailed instructions, see docs/install.txt.

View File

@ -1 +1 @@
VERSION = (0, 95, 'post-magic-removal') VERSION = (0, 96, 'pre')

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,8 @@ def user_passes_test(test_func, login_url=LOGIN_URL):
if test_func(request.user): if test_func(request.user):
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path()))) return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path())))
_checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__
return _checklogin return _checklogin
return _dec return _dec

View File

@ -61,7 +61,7 @@ class PasswordResetForm(forms.Manipulator):
except User.DoesNotExist: except User.DoesNotExist:
raise validators.ValidationError, "That e-mail address doesn't have an associated user acount. Are you sure you've registered?" raise validators.ValidationError, "That e-mail address doesn't have an associated user acount. Are you sure you've registered?"
def save(self, domain_override=None): def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
"Calculates a new password randomly and sends it to the user" "Calculates a new password randomly and sends it to the user"
from django.core.mail import send_mail from django.core.mail import send_mail
new_pass = User.objects.make_random_password() new_pass = User.objects.make_random_password()
@ -73,7 +73,7 @@ class PasswordResetForm(forms.Manipulator):
domain = current_site.domain domain = current_site.domain
else: else:
site_name = domain = domain_override site_name = domain = domain_override
t = loader.get_template('registration/password_reset_email.html') t = loader.get_template(email_template_name)
c = { c = {
'new_password': new_pass, 'new_password': new_pass,
'email': self.user_cache.email, 'email': self.user_cache.email,

View File

@ -34,9 +34,8 @@ def login(request, template_name='registration/login.html'):
def logout(request, next_page=None, template_name='registration/logged_out.html'): def logout(request, next_page=None, template_name='registration/logged_out.html'):
"Logs out the user and displays 'You are logged out' message." "Logs out the user and displays 'You are logged out' message."
from django.contrib.auth import logout from django.contrib.auth import logout
try: logout(request)
logout(request) if next_page is None:
except KeyError:
return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request)) return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
else: else:
# Redirect to this page until the session has been cleared. # Redirect to this page until the session has been cleared.
@ -50,7 +49,8 @@ def redirect_to_login(next, login_url=LOGIN_URL):
"Redirects the user to the login page, passing the given 'next' page" "Redirects the user to the login page, passing the given 'next' page"
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, next)) return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, next))
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html'): def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
email_template_name='registration/password_reset_email.html'):
new_data, errors = {}, {} new_data, errors = {}, {}
form = PasswordResetForm() form = PasswordResetForm()
if request.POST: if request.POST:
@ -58,9 +58,9 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
errors = form.get_validation_errors(new_data) errors = form.get_validation_errors(new_data)
if not errors: if not errors:
if is_admin_site: if is_admin_site:
form.save(request.META['HTTP_HOST']) form.save(domain_override=request.META['HTTP_HOST'])
else: else:
form.save() form.save(email_template_name=email_template_name)
return HttpResponseRedirect('%sdone/' % request.path) return HttpResponseRedirect('%sdone/' % request.path)
return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)}, return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request)) context_instance=RequestContext(request))

View File

@ -78,7 +78,7 @@ def get_version():
from django import VERSION from django import VERSION
v = '.'.join([str(i) for i in VERSION[:-1]]) v = '.'.join([str(i) for i in VERSION[:-1]])
if VERSION[-1]: if VERSION[-1]:
v += ' (%s)' % VERSION[-1] v += '-' + VERSION[-1]
return v return v
def get_sql_create(app): def get_sql_create(app):
@ -684,10 +684,10 @@ def get_validation_errors(outfile, app=None):
from django.db.models.fields.related import RelatedObject from django.db.models.fields.related import RelatedObject
e = ModelErrorCollection(outfile) e = ModelErrorCollection(outfile)
for (app_name, error) in get_app_errors().items(): for (app_name, error) in get_app_errors().items():
e.add(app_name, error) e.add(app_name, error)
for cls in models.get_models(app): for cls in models.get_models(app):
opts = cls._meta opts = cls._meta
connection_name = model_connection_name(cls) connection_name = model_connection_name(cls)
@ -757,7 +757,7 @@ def get_validation_errors(outfile, app=None):
if r.get_accessor_name() == rel_query_name: if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
for i, f in enumerate(opts.many_to_many): for i, f in enumerate(opts.many_to_many):
# Check to see if the related m2m field will clash with any # Check to see if the related m2m field will clash with any
# existing fields, m2m fields, m2m related objects or related objects # existing fields, m2m fields, m2m related objects or related objects

View File

@ -41,11 +41,11 @@ class DateTimeAwareJSONEncoder(simplejson.JSONEncoder):
TIME_FORMAT = "%H:%M:%S" TIME_FORMAT = "%H:%M:%S"
def default(self, o): def default(self, o):
if isinstance(o, datetime.date): if isinstance(o, datetime.datetime):
return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
elif isinstance(o, datetime.date):
return o.strftime(self.DATE_FORMAT) return o.strftime(self.DATE_FORMAT)
elif isinstance(o, datetime.time): elif isinstance(o, datetime.time):
return o.strftime(self.TIME_FORMAT) return o.strftime(self.TIME_FORMAT)
elif isinstance(o, datetime.datetime):
return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
else: else:
return super(self, DateTimeAwareJSONEncoder).default(o) return super(self, DateTimeAwareJSONEncoder).default(o)

View File

@ -201,3 +201,19 @@ class RegexURLResolver(object):
sub_match = self.reverse(viewname, *args, **kwargs) sub_match = self.reverse(viewname, *args, **kwargs)
result = reverse_helper(self.regex, *args, **kwargs) result = reverse_helper(self.regex, *args, **kwargs)
return result + sub_match return result + sub_match
def resolve(path, urlconf=None):
if urlconf is None:
from django.conf import settings
urlconf = settings.ROOT_URLCONF
resolver = RegexURLResolver(r'^/', urlconf)
return resolver.resolve(path)
def reverse(viewname, urlconf=None, args=None, kwargs=None):
args = args or []
kwargs = kwargs or {}
if urlconf is None:
from django.conf import settings
urlconf = settings.ROOT_URLCONF
resolver = RegexURLResolver(r'^/', urlconf)
return '/' + resolver.reverse(viewname, *args, **kwargs)

View File

@ -16,6 +16,18 @@ from django.utils.text import capfirst
# Admin stages. # Admin stages.
ADD, CHANGE, BOTH = 1, 2, 3 ADD, CHANGE, BOTH = 1, 2, 3
# Decorator. Takes a function that returns a tuple in this format:
# (viewname, viewargs, viewkwargs)
# Returns a function that calls urlresolvers.reverse() on that data, to return
# the URL for those parameters.
def permalink(func):
from django.core.urlresolvers import reverse
def inner(*args, **kwargs):
bits = func(*args, **kwargs)
viewname = bits[0]
return reverse(bits[0], None, *bits[1:2])
return inner
class LazyDate(object): class LazyDate(object):
""" """
Use in limit_choices_to to compare the field to dates calculated at run time Use in limit_choices_to to compare the field to dates calculated at run time

View File

@ -44,7 +44,7 @@ class ModelBase(type):
new_class._meta.app_label = model_module.__name__.split('.')[-2] new_class._meta.app_label = model_module.__name__.split('.')[-2]
# Bail out early if we have already created this class. # Bail out early if we have already created this class.
m = get_model(new_class._meta.app_label, name) m = get_model(new_class._meta.app_label, name, False)
if m is not None: if m is not None:
return m return m
@ -68,7 +68,7 @@ class ModelBase(type):
# the first class for this model to register with the framework. There # the first class for this model to register with the framework. There
# should only be one class for each model, so we must always return the # should only be one class for each model, so we must always return the
# registered version. # registered version.
return get_model(new_class._meta.app_label, name) return get_model(new_class._meta.app_label, name, False)
class Model(object): class Model(object):
__metaclass__ = ModelBase __metaclass__ = ModelBase

View File

@ -247,9 +247,9 @@ class Field(object):
params['is_required'] = not self.blank and not self.primary_key and not rel params['is_required'] = not self.blank and not self.primary_key and not rel
# BooleanFields (CheckboxFields) are a special case. They don't take # BooleanFields (CheckboxFields) are a special case. They don't take
# is_required or validator_list. # is_required.
if isinstance(self, BooleanField): if isinstance(self, BooleanField):
del params['validator_list'], params['is_required'] del params['is_required']
# If this field is in a related context, check whether any other fields # If this field is in a related context, check whether any other fields
# in the related object have core=True. If so, add a validator -- # in the related object have core=True. If so, add a validator --

View File

@ -83,11 +83,15 @@ def get_models(app_mod=None, creation_order=False):
model_list.extend(get_models(app_mod)) model_list.extend(get_models(app_mod))
return model_list return model_list
def get_model(app_label, model_name): def get_model(app_label, model_name, seed_cache = True):
""" """
Returns the model matching the given app_label and case-insensitive model_name. Returns the model matching the given app_label and case-insensitive
model_name.
Returns None if no model is found. Returns None if no model is found.
""" """
if seed_cache:
get_apps()
try: try:
model_dict = _app_models[app_label] model_dict = _app_models[app_label]
except KeyError: except KeyError:

View File

@ -434,10 +434,12 @@ class HiddenField(FormField):
(self.get_id(), self.field_name, escape(data)) (self.get_id(), self.field_name, escape(data))
class CheckboxField(FormField): class CheckboxField(FormField):
def __init__(self, field_name, checked_by_default=False): def __init__(self, field_name, checked_by_default=False, validator_list=None):
if validator_list is None: validator_list = []
self.field_name = field_name self.field_name = field_name
self.checked_by_default = checked_by_default self.checked_by_default = checked_by_default
self.is_required, self.validator_list = False, [] # because the validator looks for these self.is_required = False # because the validator looks for these
self.validator_list = validator_list[:]
def render(self, data): def render(self, data):
checked_html = '' checked_html = ''

View File

@ -358,7 +358,7 @@ class DebugParser(Parser):
super(DebugParser, self).extend_nodelist(nodelist, node, token) super(DebugParser, self).extend_nodelist(nodelist, node, token)
def unclosed_block_tag(self, parse_until): def unclosed_block_tag(self, parse_until):
(command, source) = self.command_stack.pop() command, source = self.command_stack.pop()
msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until)) msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
raise self.source_error( source, msg) raise self.source_error( source, msg)

View File

@ -50,6 +50,8 @@ class ExtendsNode(Node):
if self.parent_name_expr: if self.parent_name_expr:
error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr. error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
raise TemplateSyntaxError, error_msg raise TemplateSyntaxError, error_msg
if hasattr(parent, 'render'):
return parent
try: try:
source, origin = find_template_source(parent, self.template_dirs) source, origin = find_template_source(parent, self.template_dirs)
except TemplateDoesNotExist: except TemplateDoesNotExist:
@ -125,7 +127,7 @@ def do_block(parser, token):
if block_name in parser.__loaded_blocks: if block_name in parser.__loaded_blocks:
raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name) raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
parser.__loaded_blocks.append(block_name) parser.__loaded_blocks.append(block_name)
except AttributeError: # parser._loaded_blocks isn't a list yet except AttributeError: # parser.__loaded_blocks isn't a list yet
parser.__loaded_blocks = [block_name] parser.__loaded_blocks = [block_name]
nodelist = parser.parse(('endblock',)) nodelist = parser.parse(('endblock',))
parser.delete_first_token() parser.delete_first_token()
@ -137,8 +139,9 @@ def do_extends(parser, token):
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
uses the literal value "base" as the name of the parent template to extend, uses the literal value "base" as the name of the parent template to extend,
or ``{% extends variable %}`` uses the value of ``variable`` as the name or ``{% extends variable %}`` uses the value of ``variable`` as either the
of the parent template to extend. name of the parent template to extend (if it evaluates to a string,) or as
the parent tempate itelf (if it evaluates to a Template object).
""" """
bits = token.contents.split() bits = token.contents.split()
if len(bits) != 2: if len(bits) != 2:

View File

@ -35,6 +35,14 @@ try:
except ImportError: except ImportError:
import dummy_thread as thread import dummy_thread as thread
# This import does nothing, but it's necessary to avoid some race conditions
# in the threading module. See http://code.djangoproject.com/ticket/2330 .
try:
import threading
except ImportError:
pass
RUN_RELOADER = True RUN_RELOADER = True
def reloader_thread(): def reloader_thread():

View File

@ -25,3 +25,6 @@ def to_locale(language):
return language[:p].lower()+'_'+language[p+1:].upper() return language[:p].lower()+'_'+language[p+1:].upper()
else: else:
return language.lower() return language.lower()
def get_language_from_request(request):
return settings.LANGUAGE_CODE

View File

@ -7,7 +7,7 @@ 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=None, allow_empty=False, context_processors=None, extra_context=None, allow_empty=False, context_processors=None,
mimetype=None): mimetype=None, allow_future=False):
""" """
Generic top-level archive of date-based objects. Generic top-level archive of date-based objects.
@ -20,7 +20,8 @@ def archive_index(request, queryset, date_field, num_latest=15,
""" """
if extra_context is None: extra_context = {} if extra_context is None: extra_context = {}
model = queryset.model model = queryset.model
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()}) if not allow_future:
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()})
date_list = queryset.dates(date_field, 'year')[::-1] date_list = queryset.dates(date_field, 'year')[::-1]
if not date_list and not allow_empty: if not date_list and not allow_empty:
raise Http404, "No %s available" % model._meta.verbose_name raise Http404, "No %s available" % model._meta.verbose_name
@ -47,7 +48,7 @@ def archive_index(request, queryset, date_field, num_latest=15,
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=None, allow_empty=False, template_loader=loader, extra_context=None, allow_empty=False,
context_processors=None, template_object_name='object', mimetype=None, context_processors=None, template_object_name='object', mimetype=None,
make_object_list=False): make_object_list=False, allow_future=False):
""" """
Generic yearly archive view. Generic yearly archive view.
@ -67,8 +68,8 @@ def archive_year(request, year, queryset, date_field, template_name=None,
lookup_kwargs = {'%s__year' % date_field: year} lookup_kwargs = {'%s__year' % date_field: year}
# Only bother to check current date if the year isn't in the past. # Only bother to check current date if the year isn't in the past and future objects aren't requested.
if int(year) >= now.year: if int(year) >= now.year and not allow_future:
lookup_kwargs['%s__lte' % date_field] = now lookup_kwargs['%s__lte' % date_field] = now
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:
@ -95,7 +96,7 @@ def archive_year(request, year, queryset, date_field, template_name=None,
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=None, allow_empty=False, context_processors=None, extra_context=None, allow_empty=False, context_processors=None,
template_object_name='object', mimetype=None): template_object_name='object', mimetype=None, allow_future=False):
""" """
Generic monthly archive view. Generic monthly archive view.
@ -127,19 +128,28 @@ def archive_month(request, year, month, queryset, date_field,
last_day = first_day.replace(month=first_day.month + 1) last_day = first_day.replace(month=first_day.month + 1)
lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)} lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)}
# Only bother to check current date if the month isn't in the past. # Only bother to check current date if the month isn't in the past and future objects are requested.
if last_day >= now.date(): if last_day >= now.date() and not allow_future:
lookup_kwargs['%s__lte' % date_field] = now lookup_kwargs['%s__lte' % date_field] = now
object_list = queryset.filter(**lookup_kwargs) object_list = queryset.filter(**lookup_kwargs)
if not object_list and not allow_empty: if not object_list and not allow_empty:
raise Http404 raise Http404
# Calculate the next month, if applicable.
if allow_future:
next_month = last_day + datetime.timedelta(days=1)
elif last_day < datetime.date.today():
next_month = last_day + datetime.timedelta(days=1)
else:
next_month = None
if not template_name: if not template_name:
template_name = "%s/%s_archive_month.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_archive_month.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, {
'%s_list' % template_object_name: object_list, '%s_list' % template_object_name: object_list,
'month': date, 'month': date,
'next_month': (last_day < datetime.date.today()) and (last_day + datetime.timedelta(days=1)) or None, 'next_month': next_month,
'previous_month': first_day - datetime.timedelta(days=1), 'previous_month': first_day - datetime.timedelta(days=1),
}, context_processors) }, context_processors)
for key, value in extra_context.items(): for key, value in extra_context.items():
@ -152,7 +162,7 @@ def archive_month(request, year, month, queryset, date_field,
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=None, allow_empty=True, context_processors=None, extra_context=None, allow_empty=True, context_processors=None,
template_object_name='object', mimetype=None): template_object_name='object', mimetype=None, allow_future=False):
""" """
Generic weekly archive view. Generic weekly archive view.
@ -177,8 +187,8 @@ def archive_week(request, year, week, queryset, date_field,
last_day = date + datetime.timedelta(days=7) last_day = date + datetime.timedelta(days=7)
lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)} lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)}
# Only bother to check current date if the week isn't in the past. # Only bother to check current date if the week isn't in the past and future objects aren't requested.
if last_day >= now.date(): if last_day >= now.date() and not allow_future:
lookup_kwargs['%s__lte' % date_field] = now lookup_kwargs['%s__lte' % date_field] = now
object_list = queryset.filter(**lookup_kwargs) object_list = queryset.filter(**lookup_kwargs)
if not object_list and not allow_empty: if not object_list and not allow_empty:
@ -201,7 +211,7 @@ 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=None, allow_empty=False, template_loader=loader, extra_context=None, allow_empty=False,
context_processors=None, template_object_name='object', context_processors=None, template_object_name='object',
mimetype=None): mimetype=None, allow_future=False):
""" """
Generic daily archive view. Generic daily archive view.
@ -229,12 +239,21 @@ def archive_day(request, year, month, day, queryset, date_field,
'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)), '%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
} }
# Only bother to check current date if the date isn't in the past. # Only bother to check current date if the date isn't in the past and future objects aren't requested.
if date >= now.date(): if date >= now.date() and not allow_future:
lookup_kwargs['%s__lte' % date_field] = now lookup_kwargs['%s__lte' % date_field] = now
object_list = queryset.filter(**lookup_kwargs) object_list = queryset.filter(**lookup_kwargs)
if not allow_empty and not object_list: if not allow_empty and not object_list:
raise Http404 raise Http404
# Calculate the next day, if applicable.
if allow_future:
next_day = date + datetime.timedelta(days=1)
elif date < datetime.date.today():
next_day = date + datetime.timedelta(days=1)
else:
next_day = None
if not template_name: if not template_name:
template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name) t = template_loader.get_template(template_name)
@ -242,7 +261,7 @@ def archive_day(request, year, month, day, queryset, date_field,
'%s_list' % template_object_name: object_list, '%s_list' % template_object_name: object_list,
'day': date, 'day': date,
'previous_day': date - datetime.timedelta(days=1), 'previous_day': date - datetime.timedelta(days=1),
'next_day': (date < datetime.date.today()) and (date + datetime.timedelta(days=1)) or None, 'next_day': next_day,
}, context_processors) }, context_processors)
for key, value in extra_context.items(): for key, value in extra_context.items():
if callable(value): if callable(value):
@ -267,7 +286,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=None, context_processors=None, template_loader=loader, extra_context=None, context_processors=None,
template_object_name='object', mimetype=None): template_object_name='object', mimetype=None, allow_future=False):
""" """
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.
@ -289,8 +308,8 @@ def object_detail(request, year, month, day, queryset, date_field,
'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)), '%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
} }
# Only bother to check current date if the date isn't in the past. # Only bother to check current date if the date isn't in the past and future objects aren't requested.
if date >= now.date(): if date >= now.date() and not allow_future:
lookup_kwargs['%s__lte' % date_field] = now lookup_kwargs['%s__lte' % date_field] = now
if object_id: if object_id:
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id

123
docs/api_stability.txt Normal file
View File

@ -0,0 +1,123 @@
=============
API stability
=============
Although Django has not reached a 1.0 release, the bulk of Django's public APIs are
stable as of the 0.95 release. This document explains which APIs will and will not
change before the 1.0 release.
What "stable" means
===================
In this context, stable means:
- All the public APIs -- everything documented in the linked documents, and
all methods that don't begin with an underscore -- will not be moved or
renamed without providing backwards-compatible aliases.
- If new features are added to these APIs -- which is quite possible --
they will not break or change the meaning of existing methods. In other
words, "stable" does not (necessarily) mean "complete."
- If, for some reason, an API declared stable must be removed or replaced, it
will be declared deprecated but will remain in the API until at least
version 1.1. Warnings will be issued when the deprecated method is
called.
- We'll only break backwards compatibility of these APIs if a bug or
security hole makes it completely unavoidable.
Stable APIs
===========
These APIs are stable:
- `Caching`_.
- `Custom template tags and libraries`_ (with the possible exception for a
small change in the way templates are registered and loaded).
- `Database lookup`_ (with the exception of validation; see below).
- `django-admin utility`_.
- `FastCGI integration`_.
- `Flatpages`_.
- `Generic views`_.
- `Internationalization`_.
- `Legacy database integration`_.
- `Model definition`_ (with the exception of generic relations; see below).
- `mod_python integration`_.
- `Redirects`_.
- `Request/response objects`_.
- `Sending email`_.
- `Sessions`_.
- `Settings`_.
- `Syndication`_.
- `Template language`_ (with the exception of some possible disambiguation
of how tag arguments are passed to tags and filters).
- `Transactions`_.
- `URL dispatch`_.
You'll notice that this list comprises the bulk of Django's APIs. That's right
-- most of the changes planned between now and Django 1.0 are either under the
hood, feature additions, or changes to a few select bits. A good estimate is
that 90% of Django can be considered forwards-compatible at this point.
That said, these APIs should *not* be considered stable, and are likely to
change:
- `Forms and validation`_ will most likely be compeltely rewritten to
deemphasize Manipulators in favor of validation-aware models.
- `Serialization`_ is under heavy development; changes are likely.
- The `authentication`_ framework is changing to be far more flexible, and
API changes may be necessary.
- Generic relations will most likely be moved out of core and into the
content-types contrib package to avoid core dependacies on optional
components.
- The comments framework, which is yet undocumented, will likely get a complete
rewrite before Django 1.0. Even if the change isn't quite that drastic,
there will at least be moderate changes.
.. _caching: http://www.djangoproject.com/documentation/cache/
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/
.. _database lookup: http://www.djangoproject.com/documentation/db_api/
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
.. _internationalization: http://www.djangoproject.com/documentation/i18n/
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/
.. _model definition: http://www.djangoproject.com/documentation/model_api/
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/
.. _redirects: http://www.djangoproject.com/documentation/redirects/
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/
.. _sending email: http://www.djangoproject.com/documentation/email/
.. _sessions: http://www.djangoproject.com/documentation/sessions/
.. _settings: http://www.djangoproject.com/documentation/settings/
.. _syndication: http://www.djangoproject.com/documentation/syndication/
.. _template language: http://www.djangoproject.com/documentation/templates/
.. _transactions: http://www.djangoproject.com/documentation/transactions/
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/
.. _forms and validation: http://www.djangoproject.com/documentation/forms/
.. _serialization: http://www.djangoproject.com/documentation/serialization/
.. _authentication: http://www.djangoproject.com/documentation/authentication/

View File

@ -168,6 +168,10 @@ Please follow these coding standards when writing code for inclusion in Django:
{{foo}} {{foo}}
* Please don't put your name in the code. While we appreciate all
contributions to Django, our policy is not to publish individual
developer names in code -- for instance, at the top of Python modules.
Committing code Committing code
=============== ===============
@ -212,6 +216,10 @@ repository:
first, then the "Fixed #abc." For example: first, then the "Fixed #abc." For example:
"magic-removal: Fixed #123 -- Added whizbang feature." "magic-removal: Fixed #123 -- Added whizbang feature."
For the curious: We're using a `Trac post-commit hook`_ for this.
.. _Trac post-commit hook: http://trac.edgewall.org/browser/trunk/contrib/trac-post-commit-hook
* If your commit references a ticket in the Django `ticket tracker`_ but * If your commit references a ticket in the Django `ticket tracker`_ but
does *not* close the ticket, include the phrase "Refs #abc", where "abc" does *not* close the ticket, include the phrase "Refs #abc", where "abc"
is the number of the ticket your commit references. We've rigged is the number of the ticket your commit references. We've rigged

View File

@ -274,8 +274,8 @@ Loose coupling
A view shouldn't care about which template system the developer uses -- or even A view shouldn't care about which template system the developer uses -- or even
whether a template system is used at all. whether a template system is used at all.
Designate between GET and POST Differentiate between GET and POST
------------------------------ ----------------------------------
GET and POST are distinct; developers should explicitly use one or the other. GET and POST are distinct; developers should explicitly use one or the other.
The framework should make it easy to distinguish between GET and POST data. The framework should make it easy to distinguish between GET and POST data.

View File

@ -156,7 +156,7 @@ logical to us.
----------------------------------------------------- -----------------------------------------------------
We're well aware that there are other awesome Web frameworks out there, and We're well aware that there are other awesome Web frameworks out there, and
we're not adverse to borrowing ideas where appropriate. However, Django was we're not averse to borrowing ideas where appropriate. However, Django was
developed precisely because we were unhappy with the status quo, so please be developed precisely because we were unhappy with the status quo, so please be
aware that "because <Framework X>" does it is not going to be sufficient reason aware that "because <Framework X>" does it is not going to be sufficient reason
to add a given feature to Django. to add a given feature to Django.
@ -251,6 +251,16 @@ information than the docs that come with the latest Django release.
.. _stored in revision control: http://code.djangoproject.com/browser/django/trunk/docs .. _stored in revision control: http://code.djangoproject.com/browser/django/trunk/docs
Where can I find Django developers for hire?
--------------------------------------------
Consult our `developers for hire page`_ for a list of Django developers who
would be happy to help you.
You might also be interested in posting a job to http://www.gypsyjobs.com/ .
.. _developers for hire page: http://code.djangoproject.com/wiki/DevelopersForHire
Installation questions Installation questions
====================== ======================
@ -546,10 +556,15 @@ Set the ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting to ``True``. See the
How do I automatically set a field's value to the user who last edited the object in the admin? How do I automatically set a field's value to the user who last edited the object in the admin?
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
At this point, you can't do this. But it's an oft-requested feature, so we're At this point, Django doesn't have an official way to do this. But it's an oft-requested
discussing how it can be implemented. The problem is we don't want to couple feature, so we're discussing how it can be implemented. The problem is we don't want to couple
the model layer with the admin layer with the request layer (to get the current the model layer with the admin layer with the request layer (to get the current user). It's a
user). It's a tricky problem. tricky problem.
One person hacked up a `solution that doesn't require patching Django`_, but note that it's an
unofficial solution, and there's no guarantee it won't break at some point.
.. _solution that doesn't require patching Django: http://lukeplant.me.uk/blog.php?id=1107301634
How do I limit admin access so that objects can only be edited by the users who created them? How do I limit admin access so that objects can only be edited by the users who created them?
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
@ -614,3 +629,21 @@ To create a user, you'll have to use the Python API. See `creating users`_ for
full info. full info.
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users .. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users
Contributing code
=================
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
--------------------------------------------------------------------------------------------
Don't worry: We're not ignoring you!
It's important to understand there is a difference between "a ticket is being
ignored" and "a ticket has not been attended to yet." Django's ticket system
contains hundreds of open tickets, of various degrees of impact on end-user
functionality, and Django's developers have to review and prioritize.
Besides, if your feature request stands no chance of inclusion in Django, we
won't ignore it -- we'll just close the ticket. So if your ticket is still
open, it doesn't mean we're ignoring you; it just means we haven't had time to
look at it yet.

View File

@ -404,6 +404,43 @@ Here's a simple function that might drive the above form::
errors = new_data = {} errors = new_data = {}
form = forms.FormWrapper(manipulator, new_data, errors) form = forms.FormWrapper(manipulator, new_data, errors)
return render_to_response('contact_form.html', {'form': form}) return render_to_response('contact_form.html', {'form': form})
``FileField`` and ``ImageField`` special cases
==============================================
Dealing with ``FileField`` and ``ImageField`` objects is a little more
complicated.
First, you'll need to make sure that your ``<form>`` element correctly defines
the ``enctype`` as ``"multipart/form-data"``, in order to upload files::
<form enctype="multipart/form-data" method="post" action="/foo/">
Next, you'll need to treat the field in the template slightly differently. A
``FileField`` or ``ImageField`` is represented by *two* HTML form elements.
For example, given this field in a model::
photo = model.ImageField('/path/to/upload/location')
You'd need to display two formfields in the template::
<p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p>
The first bit (``{{ form.photo }}``) displays the currently-selected file,
while the second (``{{ form.photo_file }}``) actually contains the file upload
form field. Thus, at the validation layer you need to check the ``photo_file``
key.
Finally, in your view, make sure to access ``request.FILES``, rather than
``request.POST``, for the uploaded files. This is necessary because
``request.POST`` does not contain file-upload data.
For example, following the ``new_data`` convention, you might do something like
this::
new_data = request.POST.copy()
new_data.update(request.FILES)
Validators Validators
========== ==========

View File

@ -148,7 +148,8 @@ are views for displaying drilldown pages for date-based data.
**Description:** **Description:**
A top-level index page showing the "latest" objects, by date. Objects with A top-level index page showing the "latest" objects, by date. Objects with
a date in the *future* are not included. a date in the *future* are not included unless you set ``allow_future`` to
``True``.
**Required arguments:** **Required arguments:**
@ -185,6 +186,11 @@ a date in the *future* are not included.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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
@ -217,7 +223,8 @@ In addition to ``extra_context``, the template's context will be:
**Description:** **Description:**
A yearly archive page showing all available months in a given year. Objects A yearly archive page showing all available months in a given year. Objects
with a date in the *future* are not displayed. with a date in the *future* are not displayed unless you set ``allow_future``
to ``True``.
**Required arguments:** **Required arguments:**
@ -265,6 +272,11 @@ with a date in the *future* are not displayed.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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
@ -296,7 +308,8 @@ In addition to ``extra_context``, the template's context will be:
**Description:** **Description:**
A monthly archive page showing all objects in a given month. Objects with a A monthly archive page showing all objects in a given month. Objects with a
date in the *future* are not displayed. date in the *future* are not displayed unless you set ``allow_future`` to
``True``.
**Required arguments:** **Required arguments:**
@ -346,6 +359,11 @@ date in the *future* are not displayed.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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
@ -378,7 +396,7 @@ In addition to ``extra_context``, the template's context will be:
**Description:** **Description:**
A weekly archive page showing all objects in a given week. Objects with a date A weekly archive page showing all objects in a given week. Objects with a date
in the *future* are not displayed. in the *future* are not displayed unless you set ``allow_future`` to ``True``.
**Required arguments:** **Required arguments:**
@ -422,6 +440,11 @@ in the *future* are not displayed.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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
@ -445,7 +468,8 @@ In addition to ``extra_context``, the template's context will be:
**Description:** **Description:**
A day archive page showing all objects in a given day. Days in the future throw A day archive page showing all objects in a given day. Days in the future throw
a 404 error, regardless of whether any objects exist for future days. a 404 error, regardless of whether any objects exist for future days, unless
you set ``allow_future`` to ``True``.
**Required arguments:** **Required arguments:**
@ -501,6 +525,11 @@ a 404 error, regardless of whether any objects exist for future days.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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
@ -537,7 +566,9 @@ and today's date is used instead.
**Description:** **Description:**
A page representing an individual object. A page representing an individual object. If the object has a date value in the
future, the view will throw a 404 error by default, unless you set
``allow_future`` to ``True``.
**Required arguments:** **Required arguments:**
@ -604,6 +635,11 @@ A page representing an individual object.
* ``mimetype``: The MIME type to use for the resulting document. Defaults * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_MIME_TYPE`` setting. to the value of the ``DEFAULT_MIME_TYPE`` setting.
* ``allow_future``: A boolean specifying whether to include "future"
objects on this page, where "future" means objects in which the field
specified in ``date_field`` is greater than the current date/time. By
default, this is ``False``.
**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

@ -235,7 +235,7 @@ To pluralize, specify both the singular and plural forms with the
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and ``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
``{% endblocktrans %}``. Example:: ``{% endblocktrans %}``. Example::
{% blocktrans count list|counted as counter %} {% blocktrans count list|count as counter %}
There is only one {{ name }} object. There is only one {{ name }} object.
{% plural %} {% plural %}
There are {{ counter }} {{ name }} objects. There are {{ counter }} {{ name }} objects.

View File

@ -77,9 +77,9 @@ It's easy either way.
Installing the official version Installing the official version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Download Django-0.91.tar.gz from our `download page`_. 1. Download Django-0.95.tar.gz from our `download page`_.
2. ``tar xzvf Django-0.91.tar.gz`` 2. ``tar xzvf Django-0.95.tar.gz``
3. ``cd Django-0.91`` 3. ``cd Django-0.95``
4. ``sudo python setup.py install`` 4. ``sudo python setup.py install``
Note that the last command will automatically download and install setuptools_ Note that the last command will automatically download and install setuptools_
@ -89,14 +89,6 @@ connection.
This will install Django in your Python installation's ``site-packages`` This will install Django in your Python installation's ``site-packages``
directory. directory.
.. note::
Due to recent backwards-incompatible changes, it is strongly recommended
that you use the development version (below) for any new applications or
if you are just starting to work with Django. The 0.91 release is a
dead-end branch that is primarily of use for supporting legacy Django
applications.
.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
Installing the development version Installing the development version

126
docs/release_notes_0.95.txt Normal file
View File

@ -0,0 +1,126 @@
=================================
Django version 0.95 release notes
=================================
Welcome to the Django 0.95 release.
This represents a significant advance in Django development since the 0.91
release in January 2006. The details of every change in this release would be
too extensive to list in full, but a summary is presented below.
Suitability and API stability
=============================
This release is intended to provide a stable reference point for developers
wanting to work on production-level applications that use Django.
However, it's not the 1.0 release, and we'll be introducing further changes
before 1.0. For a clear look at which areas of the framework will change (and
which ones will *not* change) before 1.0, see the api-stability.txt file, which
lives in the docs/ directory of the distribution.
You may have a need to use some of the features that are marked as
"subject to API change" in that document, but that's OK with us as long as it's
OK with you, and as long as you understand APIs may change in the future.
Fortunately, most of Django's core APIs won't be changing before version 1.0.
There likely won't be as big of a change between 0.95 and 1.0 versions as there
was between 0.91 and 0.95.
Changes and new features
========================
The major changes in this release (for developers currently using the 0.91
release) are a result of merging the 'magic-removal' branch of development.
This branch removed a number of constraints in the way Django code had to be
written that were a consequence of decisions made in the early days of Django,
prior to its open-source release. It's now possible to write more natural,
Pythonic code that works as expected, and there's less "black magic" happening
behind the scenes.
Aside from that, another main theme of this release is a dramatic increase in
usability. We've made countless improvements in error messages, documentation,
etc., to improve developers' quality of life.
The new features and changes introduced in 0.95 include:
* Django now uses a more consistent and natural filtering interface for
retrieving objects from the database.
* User-defined models, functions and constants now appear in the module
namespace they were defined in. (Previously everything was magically
transferred to the django.models.* namespace.)
* Some optional applications, such as the FlatPage, Sites and Redirects
apps, have been decoupled and moved into django.contrib. If you don't
want to use these applications, you no longer have to install their
database tables.
* Django now has support for managing database transactions.
* We've added the ability to write custom authentication and authorization
backends for authenticating users against alternate systems, such as
LDAP.
* We've made it easier to add custom table-level functions to models,
through a new "Manager" API.
* It's now possible to use Django without a database. This simply means
that the framework no longer requires you to have a working database set
up just to serve dynamic pages. In other words, you can just use
URLconfs/views on their own. Previously, the framework required that a
database be configured, regardless of whether you actually used it.
* It's now more explicit and natural to override save() and delete()
methods on models, rather than needing to hook into the pre_save() and
post_save() method hooks.
* Individual pieces of the framework now can be configured without
requiring the setting of an environment variable. This permits use of,
for example, the Django templating system inside other applications.
* More and more parts of the framework have been internationalized, as
we've expanded internationalization (i18n) support. The Django
codebase, including code and templates, has now been translated, at least
in part, into 31 languages. From Arabic to Chinese to Hungarian to Welsh,
it is now possible to use Django's admin site in your native language.
The number of changes required to port from 0.91-compatible code to the 0.95
code base are significant in some cases. However, they are, for the most part,
reasonably routine and only need to be done once. A list of the necessary
changes is described in the `Removing The Magic`_ wiki page. There is also an
easy checklist_ for reference when undertaking the porting operation.
.. _Removing The Magic: http://code.djangoproject.com/wiki/RemovingTheMagic
.. _checklist: http://code.djangoproject.com/wiki/MagicRemovalCheatSheet1
Problem reports and getting help
================================
Need help resolving a problem with Django? The documentation in the
distribution is also available online_ at the `Django website`_. The FAQ_
document is especially recommended, as it contains a number of issues that
come up time and again.
For more personalized help, the `django-users`_ mailing list is a very active
list, with more than 2,000 subscribers who can help you solve any sort of
Django problem. We recommend you search the archives first, though, because
many common questions appear with some regularity, and any particular problem
may already have been answered.
Finally, for those who prefer the more immediate feedback offered by IRC,
there's a #django channel or irc.freenode.net that is regularly populated by
Django users and developers from around the world. Friendly people are usually
available at any hour of the day -- to help, or just to chat.
.. _online: http://www.djangoproject.com/documentation/
.. _Django website: http://www.djangoproject.com/
.. _FAQ: http://www.djangoproject.com/documentation/faq/
.. _django-users: http://groups.google.com/group/django-users
Thanks for using Django!
The Django Team
July 2006

View File

@ -427,7 +427,7 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas
author's e-mail as a normal Python string. author's e-mail as a normal Python string.
""" """
def author_name(self): def author_email(self):
""" """
Returns the feed's author's e-mail as a normal Python string. Returns the feed's author's e-mail as a normal Python string.
""" """

View File

@ -363,10 +363,15 @@ extends
Signal that this template extends a parent template. Signal that this template extends a parent template.
This tag may be used in two ways: ``{% extends "base.html" %}`` (with quotes) This tag can be used in two ways:
uses the literal value "base.html" as the name of the parent template to
extend, or ``{% extends variable %}`` uses the value of ``variable`` as the * ``{% extends "base.html" %}`` (with quotes) uses the literal value
name of the parent template to extend. ``"base.html"`` as the name of the parent template to extend.
* ``{% extends variable %}`` uses the value of ``variable``. If the variable
evaluates to a string, Django will use that string as the name of the
parent template. If the variable evaluates to a ``Template`` object,
Django will use that object as the parent template.
See `Template inheritance`_ for more information. See `Template inheritance`_ for more information.
@ -493,6 +498,11 @@ If you need to combine ``and`` and ``or`` to do advanced logic, just use nested
{% endif %} {% endif %}
{% endif %} {% endif %}
Multiple uses of the same logical operator are fine, as long as you use the
same operator. For example, this is valid::
{% if athlete_list or coach_list or parent_list or teacher_list %}
ifchanged ifchanged
~~~~~~~~~ ~~~~~~~~~

View File

@ -198,21 +198,6 @@ some things to keep in mind:
How invalid variables are handled How invalid variables are handled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In Django 0.91, if a variable doesn't exist, the template system fails
silently. The variable is replaced with an empty string::
>>> t = Template("My name is {{ my_name }}.")
>>> c = Context({"foo": "bar"})
>>> t.render(c)
"My name is ."
This applies to any level of lookup::
>>> t = Template("My name is {{ person.fname }} {{ person.lname }}.")
>>> c = Context({"person": {"fname": "Stan"}})
>>> t.render(c)
"My name is Stan ."
If a variable doesn't exist, the template system inserts the value of the If a variable doesn't exist, the template system inserts the value of the
``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` (the empty ``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` (the empty
string) by default. string) by default.
@ -357,7 +342,7 @@ django.core.context_processors.request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
``DjangoContext`` will contain a variable ``request``, which is the current ``RequestContext`` will contain a variable ``request``, which is the current
`HttpRequest object`_. Note that this processor is not enabled by default; `HttpRequest object`_. Note that this processor is not enabled by default;
you'll have to activate it. you'll have to activate it.

View File

@ -2,7 +2,8 @@
Managing database transactions Managing database transactions
============================== ==============================
Django gives you a few ways to control how database transactions are managed. Django gives you a few ways to control how database transactions are managed,
if you're using a database that supports transactions.
Django's default transaction behavior Django's default transaction behavior
===================================== =====================================
@ -144,3 +145,19 @@ Thus, this is best used in situations where you want to run your own
transaction-controlling middleware or do something really strange. In almost transaction-controlling middleware or do something really strange. In almost
all situations, you'll be better off using the default behavior, or the all situations, you'll be better off using the default behavior, or the
transaction middleware, and only modify selected functions as needed. transaction middleware, and only modify selected functions as needed.
Transactions in MySQL
=====================
If you're using MySQL, your tables may or may not support transactions; it
depends on your MySQL version and the table types you're using. (By
"table types," we mean something like "InnoDB" or "MyISAM".) MySQL transaction
peculiarities are outside the scope of this article, but the MySQL site has
`information on MySQL transactions`_.
If your MySQL setup does *not* support transactions, then Django will function
in auto-commit mode: Statements will be executed and committed as soon as
they're called. If your MySQL setup *does* support transactions, Django will
handle transactions as explained in this document.
.. _information on MySQL transactions: http://dev.mysql.com/books/mysqlpress/mysql-tutorial/ch10.html

View File

@ -81,7 +81,7 @@ the following output on the command line::
Validating models... Validating models...
0 errors found. 0 errors found.
Django version 0.95 (post-magic-removal), using settings 'mysite.settings' Django version 0.95, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/ Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows). Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows).

View File

@ -54,7 +54,8 @@ http://127.0.0.1:8000/admin/. You should see the admin's login screen:
Enter the admin site Enter the admin site
==================== ====================
Now, try logging in. You should see the Django admin index page: Now, try logging in. (You created a superuser account in the first part of this
tutorial, remember?) You should see the Django admin index page:
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin02t.png .. image:: http://media.djangoproject.com/img/doc/tutorial/admin02t.png
:alt: Django admin index page :alt: Django admin index page

View File

@ -198,7 +198,7 @@ By default, the ``object_detail`` generic view uses a template called
``vote()``. ``vote()``.
Similarly, the ``object_list`` generic view uses a template called Similarly, the ``object_list`` generic view uses a template called
``<app name>/<module name>_list.html``. Thus, rename ``poll/index.html`` to ``<app name>/<module name>_list.html``. Thus, rename ``polls/index.html`` to
``polls/poll_list.html``. ``polls/poll_list.html``.
Because we have more than one entry in the URLconf that uses ``object_detail`` Because we have more than one entry in the URLconf that uses ``object_detail``
@ -206,7 +206,7 @@ for the polls app, we manually specify a template name for the results view:
``template_name='polls/results.html'``. Otherwise, both views would use the same ``template_name='polls/results.html'``. Otherwise, both views would use the same
template. Note that we use ``dict()`` to return an altered dictionary in place. template. Note that we use ``dict()`` to return an altered dictionary in place.
In previous versions of the tutorial, the templates have been provided with a context In previous parts of the tutorial, the templates have been provided with a context
that contains the ``poll` and ``latest_poll_list`` context variables. However, that contains the ``poll` and ``latest_poll_list`` context variables. However,
the generic views provide the variables ``object`` and ``object_list`` as context. the generic views provide the variables ``object`` and ``object_list`` as context.
Therefore, you need to change your templates to match the new context variables. Therefore, you need to change your templates to match the new context variables.

View File

@ -14,34 +14,20 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools. This file can also be run as a script to install or upgrade setuptools.
""" """
import sys import sys
DEFAULT_VERSION = "0.6a10" DEFAULT_VERSION = "0.6c1"
DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3] DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = { md5_data = {
'setuptools-0.5a13-py2.3.egg': '85edcf0ef39bab66e130d3f38f578c86', 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.5a13-py2.4.egg': 'ede4be600e3890e06d4ee5e0148e092a', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6a1-py2.3.egg': 'ee819a13b924d9696b0d6ca6d1c5833d', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6a1-py2.4.egg': '8256b5f1cd9e348ea6877b5ddd56257d', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6a10-py2.3.egg': '162d8357f1aff2b0349c6c247ee62987', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6a10-py2.4.egg': '803a2d8db501c1ac3b5b6fb4e907f788', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6a10dev_r42346-py2.3.egg': 'a7899272cfceb6aa60094ae8928b8077', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6a10dev_r42346-py2.4.egg': '5d42a64adca9aedb409f83ecf22156a5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6a2-py2.3.egg': 'b98da449da411267c37a738f0ab625ba', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6a2-py2.4.egg': 'be5b88bc30aed63fdefd2683be135c3b', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6a3-py2.3.egg': 'ee0e325de78f23aab79d33106dc2a8c8',
'setuptools-0.6a3-py2.4.egg': 'd95453d525a456d6c23e7a5eea89a063',
'setuptools-0.6a4-py2.3.egg': 'e958cbed4623bbf47dd1f268b99d7784',
'setuptools-0.6a4-py2.4.egg': '7f33c3ac2ef1296f0ab4fac1de4767d8',
'setuptools-0.6a5-py2.3.egg': '748408389c49bcd2d84f6ae0b01695b1',
'setuptools-0.6a5-py2.4.egg': '999bacde623f4284bfb3ea77941d2627',
'setuptools-0.6a6-py2.3.egg': '7858139f06ed0600b0d9383f36aca24c',
'setuptools-0.6a6-py2.4.egg': 'c10d20d29acebce0dc76219dc578d058',
'setuptools-0.6a7-py2.3.egg': 'cfc4125ddb95c07f9500adc5d6abef6f',
'setuptools-0.6a7-py2.4.egg': 'c6d62dab4461f71aed943caea89e6f20',
'setuptools-0.6a8-py2.3.egg': '2f18eaaa3f544f5543ead4a68f3b2e1a',
'setuptools-0.6a8-py2.4.egg': '799018f2894f14c9f8bcb2b34e69b391',
'setuptools-0.6a9-py2.3.egg': '8e438ad70438b07b0d8f82cae42b278f',
'setuptools-0.6a9-py2.4.egg': '8f6e01fc12fb1cd006dc0d6c04327ec1',
} }
import sys, os import sys, os
@ -56,7 +42,7 @@ def _validate_md5(egg_name, data):
% egg_name % egg_name
) )
sys.exit(2) sys.exit(2)
return data return data
def use_setuptools( def use_setuptools(
@ -72,7 +58,7 @@ def use_setuptools(
be the number of seconds that will be paused before initiating a download, be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed, should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script. an attempt to abort the calling script.
""" """
try: try:
import setuptools import setuptools
@ -159,7 +145,7 @@ def main(argv, version=DEFAULT_VERSION):
egg = download_setuptools(version, to_dir=tmpdir, delay=0) egg = download_setuptools(version, to_dir=tmpdir, delay=0)
sys.path.insert(0,egg) sys.path.insert(0,egg)
from setuptools.command.easy_install import main from setuptools.command.easy_install import main
main(list(argv)+[egg]) return main(list(argv)+[egg]) # we're done here
finally: finally:
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
else: else:
@ -187,7 +173,7 @@ def main(argv, version=DEFAULT_VERSION):
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames): def update_md5(filenames):
"""Update our built-in md5 registry""" """Update our built-in md5 registry"""
@ -196,7 +182,7 @@ def update_md5(filenames):
for name in filenames: for name in filenames:
base = os.path.basename(name) base = os.path.basename(name)
f = open(name,'rb') f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest() md5_data[base] = md5(f.read()).hexdigest()
f.close() f.close()

View File

@ -14,37 +14,7 @@ setup(
packages = find_packages(exclude=['examples', 'examples.*']), packages = find_packages(exclude=['examples', 'examples.*']),
package_data = { package_data = {
'': ['*.TXT'], '': ['*.TXT'],
'django.conf': ['locale/ar/LC_MESSAGES/*', 'django.conf': ['locale/*/LC_MESSAGES/*'],
'locale/bn/LC_MESSAGES/*',
'locale/cs/LC_MESSAGES/*',
'locale/cy/LC_MESSAGES/*',
'locale/da/LC_MESSAGES/*',
'locale/de/LC_MESSAGES/*',
'locale/el/LC_MESSAGES/*',
'locale/en/LC_MESSAGES/*',
'locale/es/LC_MESSAGES/*',
'locale/es_AR/LC_MESSAGES/*',
'locale/fr/LC_MESSAGES/*',
'locale/gl/LC_MESSAGES/*',
'locale/hu/LC_MESSAGES/*',
'locale/he/LC_MESSAGES/*',
'locale/is/LC_MESSAGES/*',
'locale/it/LC_MESSAGES/*',
'locale/ja/LC_MESSAGES/*',
'locale/nl/LC_MESSAGES/*',
'locale/no/LC_MESSAGES/*',
'locale/pl/LC_MESSAGES/*',
'locale/pt_BR/LC_MESSAGES/*',
'locale/ro/LC_MESSAGES/*',
'locale/ru/LC_MESSAGES/*',
'locale/sk/LC_MESSAGES/*',
'locale/sl/LC_MESSAGES/*',
'locale/sr/LC_MESSAGES/*',
'locale/sv/LC_MESSAGES/*',
'locale/ta/LC_MESSAGES/*',
'locale/uk/LC_MESSAGES/*',
'locale/zh_CN/LC_MESSAGES/*',
'locale/zh_TW/LC_MESSAGES/*'],
'django.contrib.admin': ['templates/admin/*.html', 'django.contrib.admin': ['templates/admin/*.html',
'templates/admin_doc/*.html', 'templates/admin_doc/*.html',
'templates/registration/*.html', 'templates/registration/*.html',

View File

@ -410,6 +410,12 @@ TEMPLATE_TESTS = {
# Three-level inheritance with {{ block.super }} from parent and grandparent # Three-level inheritance with {{ block.super }} from parent and grandparent
'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1_ab3_'), 'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1_ab3_'),
# Inheritance from local context without use of template loader
'inheritance24': ("{% extends context_template %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")}, '1234'),
# Inheritance from local context with variable parent template
'inheritance25': ("{% extends context_template.1 %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': [template.Template("Wrong"), template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")]}, '1234'),
### I18N ################################################################## ### I18N ##################################################################
# {% spaceless %} tag # {% spaceless %} tag

View File

@ -0,0 +1,47 @@
"Unit tests for reverse URL lookup"
from django.core.urlresolvers import reverse_helper, NoReverseMatch
import re
test_data = (
('^places/(\d+)/$', 'places/3/', [3], {}),
('^places/(\d+)/$', 'places/3/', ['3'], {}),
('^places/(\d+)/$', NoReverseMatch, ['a'], {}),
('^places/(\d+)/$', NoReverseMatch, [], {}),
('^places/(?P<id>\d+)/$', 'places/3/', [], {'id': 3}),
('^people/(?P<name>\w+)/$', 'people/adrian/', ['adrian'], {}),
('^people/(?P<name>\w+)/$', 'people/adrian/', [], {'name': 'adrian'}),
('^people/(?P<name>\w+)/$', NoReverseMatch, ['name with spaces'], {}),
('^people/(?P<name>\w+)/$', NoReverseMatch, [], {'name': 'name with spaces'}),
('^people/(?P<name>\w+)/$', NoReverseMatch, [], {}),
('^hardcoded/$', 'hardcoded/', [], {}),
('^hardcoded/$', 'hardcoded/', ['any arg'], {}),
('^hardcoded/$', 'hardcoded/', [], {'kwarg': 'foo'}),
('^people/(?P<state>\w\w)/(?P<name>\w+)/$', 'people/il/adrian/', [], {'state': 'il', 'name': 'adrian'}),
('^people/(?P<state>\w\w)/(?P<name>\d)/$', NoReverseMatch, [], {'state': 'il', 'name': 'adrian'}),
('^people/(?P<state>\w\w)/(?P<name>\w+)/$', NoReverseMatch, [], {'state': 'il'}),
('^people/(?P<state>\w\w)/(?P<name>\w+)/$', NoReverseMatch, [], {'name': 'adrian'}),
('^people/(?P<state>\w\w)/(\w+)/$', NoReverseMatch, ['il'], {'name': 'adrian'}),
('^people/(?P<state>\w\w)/(\w+)/$', 'people/il/adrian/', ['adrian'], {'state': 'il'}),
)
def run_tests(verbosity=0):
for regex, expected, args, kwargs in test_data:
passed = True
try:
got = reverse_helper(re.compile(regex), *args, **kwargs)
except NoReverseMatch, e:
if expected != NoReverseMatch:
passed, got = False, str(e)
else:
if got != expected:
passed, got = False, got
if passed and verbosity:
print "Passed: %s" % regex
elif not passed:
print "REVERSE LOOKUP FAILED: %s" % regex
print " Got: %s" % got
print " Expected: %r" % expected
if __name__ == "__main__":
run_tests(1)